Please upgrade your web browser now. Internet Explorer 6 is no longer supported.
Thinking Web Solutions?
We create smart, fun, functional websites that make your web a better place.

Archive for tag: WSS

Introducing SPThemes.com - A new store for SharePoint Themes

Today we are doing a soft launch of our new store for SharePoint Themes.

spthemes

Creating SharePoint themes is fairly complicated unless you have a pretty deep understanding of the many thousands of core SharePoint CSS classes. I commonly see quotes of over $5000 for one theme!

We are hoping to fill a market gap at a better price point, and solve a few of your SharePoint branding headaches.

Change a SharePoint web application URL

UPDATED Feb 5th:

Just noticed that if you install SP1 for WSS there is a new STSADM command called renamesite.

Check out the details here:

http://technet2.microsoft.com/windowsserver/WSS/en/library/aeb73d9b-1734-4a62-9bbb-f092ee4f58e11033.mspx?mfr=true

This seems like it should be an easy task, but always makes me think for a few minutes before I work it out. Here are a couple of simple steps to show you (and me) how its done.

1/ Open up central admin and browse to 'Alternate Access Mappings' found under the operations tab.

aam

2/ Click the 'view' drop down on the top right of the URL list and select 'Change Alternate Access Mapping Collection'. Then select the web application with the URL you would like to modify.

aamselect

3/ Select 'Edit Public URLs' and change the URL's for any zones necassary. Although the concept of zones is more semantic than anything, they do represent the different URL's a web application may be accessed by (and should suggest a use eg intranet zone for an intranet web app).

In my example I am changing http://wsstest to http://wssfun.

aamzone

4/ The final step is to update IIS to reflect the changes. Open up IIS manager (start | run | inetmgr) and browse to the web site that corresponds to the zone you updated. Right click the properties, select the web site tab and click advanced. Now change the host header to the new value.

aamiis

The web application should now be using the new URL.

How to Customize the User Information Page (3/3)

UPDATED: Changed the Edit Button to override the default SP edit button to fix edit link bug. The sample solution has been updated to reflect the changes.

Continuing on from the User Information page customization series:

Part 1 talked about some of the difficulties presented with application pages in general.

Part 2 demonstrated how to redirect the user to a custom User Profile page using feature deployment.

This part will show how you can simply customize the look and feel of the User Profile page. Before reading this part you should read Part 2 and have a look at the sample code.

I am going to focus on the more significant changes - all the HTML/CSS changes including the custom master page have been left out (The code used is included in the sample solution though).

In order to reuse as much of what SharePoint provides as possible - I will base my customized User Profile page on the SharePoint userdisp.aspx page.

The first thing to do is to turn the userprofile.aspx page created in Part 2 into code behind page that extends the UserDisplayEditPageBase. To do this, create a class like so:
public class UserProfilePage : UserDisplayEditPageBase

To resolve the UserDisplayEditPageBase class you need a reference to Microsoft.SharePoint.ApplicationPages.dll which is not in the GAC. You will need to Browse to the location "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\CONFIG\BIN" to find this dll.

I have included the dll in the sample source code for simplicity.

Now I will update my custom profile page to inherit from this class:
<%@ Page Language="C#" Inherits="CustomUserProfile.UserProfilePage, CustomUserProfile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1ba1bfabc83c4398" MasterPageFile="/_layouts/application.master" %>

Note that that I have used the fully qualified name and this assumes that the dll will be signed and placed in the GAC. See the sample solution for how this can be easily done.

Now put the contents of the original userdisp.aspx (from the 12 Hive) into the userprofile.aspx page. The first modification to make is to remove the delegate control in the custom page or so you don't get stuck in an infinite redirect loop.

At this point I could just apply a custom master page and be done. This would let me easily apply a new look and feel, but I'd like to make a few modifcations to the layout as well.

You will probably have already noticed that the guts of the page are contained in a FormComponent control. This control provides a set of properties and methods that make it easy to display a form based on a SharePoint list item. It's rendering is controlled by the TemplateName property which is currently set to 'UserListForm'. We can find the contents of this form by looking in c:\program files\common files\microsoft shared\web server extensions\12\template\controltemplates\DefaultTemplates.ascx. I don't want to modify this file directly (it will affect all SharePoint sites) so I will take a copy of the SharePoint:RenderingTemplate and put it in a new ascx file named CustomTemplates.ascx (this will live in the controltemplates dir). I then give it a custom name so that it only overrides it for the profile display page. Now I should have a FormComponent in my profile page like:
<SharePoint: FormComponent id="UserListForm" TemplateName="CustomUserListForm" ControlMode="Display" runat="server"/>

and in CustomTemplates.ascx file I have:
<SharePoint: RenderingTemplate ID="CustomUserListForm" runat="server">
<Template>
<span id="part1">
.....

Note that the TemplateName of the FormComponent matches up with the ID off the RenderingTemplate.

From here it is easy enough to edit some of the basic HTML elements of the form.

Next up is to customize the Toolbar. To do this copy the RenderingTemplate with the ID of "UserInfoListDisplayFormToolBar" from DefaultTemplates.ascx to the CustomTemplates.ascx file. I want to remove some of the buttons and have a custom Edit Item button so I can link to a custom user profile edit page. To do this change the control declaration from:
<SharePoint :UserInfoListFormToolBar runat="server"/>

to
<SharePoint :FormComponent id="UserToolBar" TemplateName="CustomUserInfoListDisplayFormToolBar" runat="server"/>

And then customize the RenderingTemplate like so:

<SharePoint :RenderingTemplate ID="CustomUserInfoListDisplayFormToolBar" runat="server">
<Template >
<script>
recycleBinEnabled = <SharePoint:ProjectProperty Property="RecycleBinEnabled" runat="server"/>;
</script>

<wssuc :ToolBar CssClass="ms-toolbar" id="toolBarTbl" runat="server" FocusOnToolbar="true">
<Template_Buttons >
<prof:UserProfileEditButton ID="UserProfileEditButton1 " runat="server " /> <SharePoint :UserInfoListDeleteItemButton ID="UserInfoListDeleteItemButton1" runat="server"/>
</Template_Buttons >
</wssuc: ToolBar>
</Template >
</
SharePoint: RenderingTemplate>

Note that I have removed a few of the buttons for alerts and regional settings as well as create a custom button for linking to the useredit.aspx.

The custom edit button simply overrides the standard SharePoint edit button and updates the URL:

 

public class UserProfileEditButton: UserInfoListEditItemButton
{
protected override void OnLoad(EventArgs
e)
{
base
.OnLoad(e);
base.NavigateUrl = base.NavigateUrl.Replace("useredit", "usermodify"
);
}
}

Next I want to tidy up up the user display name as it includes the domain and won't have any meaning for most users.

To do this I override the OnPreRender method in the UserProfilePage class created earlier:
protected override void OnPreRender(EventArgs e)
{
base.LabelTitle.Text = base.UserListForm.ListItem.GetFormattedValue("Title"
);
}

This will give us a nicer looking display name:

userinfo

In this case I don't want to display the SIP Address, so the next task is to remove this from the profile page. Currently all of the fields are rendered using a FieldIterator which is declared on the page as:
<SharePoint:ListFieldIterator runat="server"/>

In order to customize how the user list fields are displayed - we need to create a class that overrides the ListFieldIterator. My class is declared like this:

public class UserProfileFieldIterator : ListFieldIterator

Becuase I want to exclude the SIP Address I am then going to add override the IsFieldExcluded method like so:
protectedoverride boolIsFieldExcluded( SPField field)
{
if (field.Title == "SIP Address")
return true;

 

return base.IsFieldExcluded(field);
}

To replace the existing field Iterator currently being used I need to first register the assembly:
<%@ Register TagPrefix="prof" Namespace="CustomUserProfile" Assembly="CustomUserProfile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1ba1bfabc83c4398" %>

and then replace the field iterator declaration with the custom one just made:
<prof:UserProfileFieldIterator ID="UserIterator" runat="server" />

The last thing I'm going to do is attach the custom master page. This is simply a case of changing userprofile.aspx to point to a new master page:

 

<%@ Page Language="C#" Inherits="CustomUserProfile.UserProfilePage, CustomUserProfile, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1ba1bfabc83c4398" MasterPageFile="/_layouts/customprofile.master" %>

The custom master page also requires a couple of css files to override and add styles to the SharePoint page.

And now after all our hard work - "The final product":

customprofilepage

You can download the sample code (including solution deployment) -> here.

Although I have only customized the SharePoint user info page you can do the same for the edit page - or any application page really.

I am considering doing a Part 4 with some advanced customizations such as having a custom action when closing the page (eg redirect) and modifying some of the form fields. Let me know if this is something that you are interested in.

For more information on Form Components, Rendering Templates and List Iterators have a look at the following resource:

http://msdn2.microsoft.com/en-us/library/aa544154.aspx

How to Customize the User Information Page (2/3)

Liam Cleary recently wrote a blog post on how to "Hide the My Settings link" which is part of the welcome menu. Although his solution is perfectly valid and probably quicker and easier than the alternative solution that I offer - It did have some issues.
Firstly he edited the welcome.ascx user control. Like editing an application page this will affect all sites for that instance of SharePoint.
Secondly this only covers the scenario where the user actually clicks on the "My Settings" link. There are many other access points to the User Information page. For example any list edit/display form will have a link to the page. Thats any instance where you see the following audit info:
auditinfo

So creating a custom "My Settings" link only covers half the problem.

The question then is - how to create a solution that doesn't involve editing shared resources and covers every possible link scenario??
The answer lies in the userdisp.aspx page. In the AdditionalPageHead placehoder tag you will find the following control declaration:
<SharePoint: DelegateControl runat="server" id="DelctlProfileRedirection" ControlId="ProfileRedirection" Scope="Farm" />

What we need to do is create a user control, that redirects the user to a custom page. The user control will the be activated via a feature.

Brief Steps:

  1. Create a custom user display page and place it in the LAYOUTS directory.
  2. Create a new User Control that has code to redirect the user to your custom page.
  3. Create a feature for the delegate control. This feature will cause your custom control to run in place of the SharePoint:DelegateControl declaration. The feature should be scoped to "farm".

I have kept the steps very brief as I have a sample solution with all the necassary code in it.
Download the sample code from here:
http://zac.provoke.co.nz/Samples/CustomUserProfile.zip

To run the sample, copy CustomUserProfile.wsp file and Deploy.cmd to your WSS server and run the following command from a prompt:
deploy.cmd http://yoursitenamehere

Once the feature is activated, navigating to My Settings page will redirect the user to userprofile.aspx.
redirectedprofile

Not a very useful custom profile page, but thats what part 3 is for :)

The only problem now is that the feature's scope is set to farm, so the user control will be run for all sites. To solve this you can create a second feature, and then before redirecting check for the features existance. This is also included in the sample solution.

Part 3 will show how to easily customise the actual User Profile page.

For more info on delegate controls have a look at the following resources:
http://msdn2.microsoft.com/en-us/library/ms470880.aspx
http://sharepointnutsandbolts.blogspot.com/2007/06/using-delegate-control.html

How to Customize the User Information Page (1/3)

This 3 part post series discusses how you would go about customizing the user profile page accessed from the "My Settings" link in the welcome menu.

That's this page here:

userprofile

Although it seems like a simple task, there is actually quite a lot to consider. I am also planning to demonstrate a number of useful SharePoint techniques along the way.

The first part is about application pages in general and why they cause headaches for designers trying to customize the look and feel of SharePoint.

Application Pages

One of the first things people want to do after installing SharePoint - is to make it look like anything but SharePoint.

If you have ever been involved in skinning a SharePoint/WSS site you will know the difficulties posed by application pages. These are the pages in the "_layouts" directory that are shared across all SharePoint sites. It is always recommended to NOT edit these files as all web applications will be affected by the changes.

This is not a problem for most application pages as they are generally only accessed by site administrators. There are however a few application pages that are viewable by all users. It can be very frustrating to find that after you have gone to all the trouble of skinning a SharePoint site only to find that a few pages still render that standard SharePoint blue.

Two commonly problematic pages are:

UserDisp.aspx

This is the user profile page shown in the above screenshot, it links to a number of other application pages such as UserEdit.aspx. The page is accessible by any authenticated user. It is also the focus of this post series.

SearchResults.aspx

The standard WSS search page. In MOSS there is also the osssearchresults.aspx page for the standard "This List" and "This Site" search scopes.

There are a few techniques that can be used to customize these pages. The easiest way would be to simply manipulate the pages in the LAYOUTS directory. Obviously any changes made are going to affect all your SharePoint sites.

It also presents a problem during deployment, how are these changes going to be packaged up to make releases easy? What if there are multiple front end servers? And what happens when a service pack comes out that has new versions of these pages? Its probable that your changes will be overwritten.

Luckily the creators of SharePoint had foreseen these issues and in most cases have provided a way to customize these pages safely. More about this in Part 2.

Part 2 will demonstrate how we can manipulate the My Settings Link

Multiple content databases - which is the default?

One awesome new feature in WSS V3 is the ability to have multiple content databases per web application.

This can help make your databases more manageable if you have some really big sites.

One real life scenario that I have seen recently is with a client that has a WSS deployment with about 15 site collections. Most of the site collections are less than 1GB in size. However there is one site collection that is around 15GB. We decided to lump all the small site collections in one content database, and dedicate another content database to the large site collection.

This has a number of benefits around scalability, ease of backup and restore, increased flexibilty for DB migration and so on.

But after we have setup our content databases how do we ensure that any new site collections created will go in the right content database?

At first I thought any new site collections were created in the content database that was attatched last.

The real answer though is that any new site collection will be created in the database with the biggest difference between existing sites and maximum number of sites allowed.

Going back to my previous example which has the following setup:

 

DB Name

Num Sites

Max Sites

LargeSiteDB

1

50

SmallSitesDB

15

50

 

In this situation any new site collections will be created in the LargeSiteDB. I want this site collection to host just the one database so this is no good.

To remedy this there are two options:

  1. Set the LargeSiteDB status to offline. This doesn't take the DB offline - it just prevents the creation of new site collections.
  2. Reduce the Max Sites on LargeSiteDB to less than 35.

I would reccommend doing a combination, it would be good practice to set LargeSiteDB to offline anyway, and lowering the Max Sites value is a good indicator to other administrators.

Move a WSS sub site to the top level

Had a situation recently where I needed to promote a subsite to a top level site. As STSADM backup/restore can only be used at the site collection level, a different approach was required.
As of WSS v3, there are two new STSADM commands - import and export.
The basic procedure to promote the subsite was to STSADM export the site to a file and then STSADM import it at the toplevel.
Here are the basic commands I used:

Export subsite

stsadm -o export -url http://toplevel/subsite -filename c:\subsite.bak -includeusersecurity -versions 4

Import subsite

stsadm -o import -url http://toplevel/ -filename c:\subsite.bak -includeusersecurity

A couple of things to note:

The -includeusersecurity switch ensures that all the columns such as modifed by, created by are maintained.
The -versions 4 switch will ensure that all versions of list items/documents are exported.
You must import/export to sites that share the same template. So a team site can only be exported and imported into another team site. You will know you are trying to import into non matching templates if you get the following error:
"The exported site is based on the template STS#1 but the destination site is based on the template STS#0"
This means that a site based on the blank template is trying to be imported into a site based on the team template. To my knowledge there is no easy way of changing a sites templates.

The following lists some of the common WSS templates:
Team Site STS#0
Blank Site STS#1
Document Workspace STS#2
Wiki Site WIKI#0
Blog BLOG#0
If you want to know more there is a wealth of information on a blog post by Jackie Bodines.

Migrating SharePoint Users with PowerShell

So I have finally jumped on the PowerShell bandwagon and created my first (useful) script. I had a need to migrate a bunch of users from local machine accounts to domain accounts. So I had a look at what stsadm commands were available and noticed enumusers and migrateusers. I figured I would be able to create a little PowerShell script that went through all the users and migrated each one. I did have the advantage of knowing that the logins were the same on the local machine as they were on the domain. A more sophisticated migration might have some xml look-up file to migrate to new login names.

The command .exe -o enumusers -url http://wsstestsite will return an xml string of users from the site http://wsstestsite.
The xml looks something like:

<Users>
  <User>
     <Login>domain\user</Login>
     <Email>user@domain.com</Email>
     <Name>SharePoint User</Name>
  </User>
  <User>
  ...
  ...
</Users>

And the script I wrote looks something like this:

# extract all users into an xml file
[xml] $users = c:\"program files\common files\microsoft shared\web server extensions\12\bin\"stsadm.exe -o enumusers -url http://wsstestsite

#iterate through each user (note the dot notation for accessing nodes)
foreach($login in $users.Users.User)
{
# determine old and new login (using regular .Net string func)
$oldlogin = $login.Login
$newlogin = $login.Login.Replace("LocalMachine", "Domain")

# migrate user
c:\"program files\common files\microsoft shared\web server extensions\12\bin\"stsadm.exe -o migrateuser -oldlogin $oldlogin -newlogin $newlogin
}

So the code basically converts each user login from the machinename\login to domainname\login.
I realise that you could probably write this a lot cleaner and simpler, but it was just thrown together in 15mins and I wasn't too worried about being elegant.

If you haven't had a chance to try out PowerShell I highly recommend you have a look. You can download it from here: http://www.microsoft.com/windowsserver2003/technologies/management/powershell/default.mspx
There are HEAPS of resources around the web on PowerShell so it's really easy to get started.

SPSecurityTrimmedControl - possible values for PermissionsString

A while back I blogged about how you could use the SPSecurityTrimmedControl to manage the display of content based on user permissions. The PermissionsString is where you specify what permissions the user must have to display the content. Until now I wasn't sure what values you could use, and the SDK although much imporoved wasn't exactly forthcoming. While using a reflector I managed to track down the SPBasePermissions enum which reveals all the possible values.

And here they are grouped in the same way you would find them in the SharePoint permissions list:

List Permissions

Site Permissions

Personal Permissions

And an example of usage if you are reading this and haven't seen the control in action before:

<SharePoint:SPSecurityTrimmedControl PermissionsString="AddAndCustomizePages, ManageLists" runat="server"> 
    <%-- some content here ... %>
</SharePoint:SPSecurityTrimmedControl>

Removing web parts from the gallery when feature deactivated

Although I love the solution framework I do have one gripe. It doesn't remove the web parts deployed to the gallery when the solution is retracted. To address this I have written a little custom code in a feature deactivation event:

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
  // manually delete web parts from the gallery

  using (SPSite site = (SPSite)properties.Feature.Parent)
  {
    using (SPWeb web = site.OpenWeb()) 
    {
      SPList list = web.Lists["Web Part Gallery"];

      // go through the items in reverse
      for (int i = list.ItemCount -1; i >= 0; i--)
      {
        // delete web parts that have been added
        if (list.Items[i].Name == "Login.webpart")
        {
          list.Items[i].Delete();
        }
      }
    }
  }
}

Note that I have written this with 'using' clauses to ensure that the web and site objects are properly disposed of after use. I also iterate through the collection in reverse in case I want to delete many items.

To make the code run I will have to add something like the following to the feature.xml

ReceiverAssembly="Provoke.UserAdminFeatures, Version=1.0.0.0, Culture=neutral, PublicKeyToken=083c78f3ca84e8af"
ReceiverClass="Provoke.UserAdminFeatures.FeatureActivation"

More details about adding events to features can be found in the WSS SDK here.