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: Development

Installation accounts for a dev/test server

I don't particularly enjoy installing SharePoint, I've done it a million times and really it's quite a boring process. When I do end up having to install it, I generally use a simple 4 account method:

  1. Admin (SPAdmin) - Used for installation and administration of SharePoint
  2. App Pool (SPAppPool) - all web apps except central admin run with this account as their identity
  3. Services (SPService) - App pool account for central admin and the SSP service account
  4. Search (SPSearch) - Used for all search services

Generally I'm installing SharePoint for my own dev/test purposes, so this suits just fine. I think it is also fine for small scale installations or instances when the admin in charge of account creation isn't interested in creating 8+ accounts. To be honest I think a lot of the time using the 8+ accounts is a tad overkill, and people are just blindly following 'best practices' without applying them to their specific environment. But being more of a dev type than an infrastructure type I don't feel qualified to formally make that recommendation.

Even for a development server it is really important to have some separation of accounts. It is in your best interest to simulate a production scenario as best you can. Take for example if you were to use the SharePoint admin account as an application pool identity. You may have permissions to do things on your dev server (in code) that will probably have issues in a production scenario.

Turn on Approval for all publishing sites

Just posting a quick script for updating all 'Pages' libraries to have approval turned on. To create the script use a console app and add references to Microsoft.SharePoint.dll and Microsoft.SharePoint.Publishing.dll. Enjoy!

// get site collection url
if (args.Length != 1)

Console.WriteLine("Please supply a site collection URL"); 
return;
}

string siteName = args[0];

// iterate through each sub site
SPSite site = new SPSite(siteName);
foreach (SPWeb web in site.AllWebs)

// look for a pages lib 
PublishingWeb pubWeb = PublishingWeb.GetPublishingWeb(web);
try 

// turn on approval 
SPList pages = pubWeb.PagesList; 
pages.EnableModeration = true; 
pages.Update(); 
Console.WriteLine("Updated site: {0}" , web.Title); 

// ignore errors that occur if not a publshing site 
catch { continue; }
}
Console.WriteLine("Finished ...");

Overriding Core Javascript Functions in SharePoint

Recently I spent a number of hours banging my head against a wall trying to customize some of the rich DHTML functionality in SharePoint. Basically I wanted to remove the Workflows option from the default list item context menu (Edit Control Block).

WithWorkflow

I couldn't use a feature as this doesn't work for the built in elements and I didn't want to mess with CORE.js as it's never good practive and would effect all doc libs. So I worked out which method to override and then added this code inside PlaceHolderMain for the allitems.aspx page like so:

<script type="text/javascript">

function AddWorkflowsMenuItem(m, ctx)
{
//do nothing
}

</script>

The idea is that it will override the defualt behaviour of adding the menu with nothing - and hence not show the menu. For some reason this didn't work, in fact when debugged, the JavaScript was throwing an error. Interestingly it worked in FireFox so I knew I wasn't doing anything wrong. Initially I thought maybe this could be a browser compat thing but overriding a method is the same in any browser.

Eventually it dawned on me that the problem was the core.js script had not yet been loaded. The culprit was the ScriptLink control:

<SharePoint:ScriptLink language="javascript" name="core.js" Defer="true" runat="server"/>

Basically this control is referencing the CORE.js external javascript file. However becuase the defer tag is specified it is loaded after my function override is declared. This deferred loading is usually a good thing as the page doesn't have to halt it's loading for the script - instead it is loading in the background. This gives a perceived performance boost to the user.

In my case I was able to add the scriptlink without the defer tag to just the page I needed to (allitems.aspx). All other pages in the site can still benefit from the deferred loading and I get the desired functionality:

WithOutWorkflow

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

My First SharePoint PowerShell Cmdlet

The Problem:

I am always using PowerShell to inspect the SharePoint object model when diagnosing problems or gathering information about an installation. One thing that annoys me is the command to get an SPSite object. This would be something like:

$site = New-Object Microsoft.SharePoint.SPSite("http://site")

I seem to be typing this command many times a day. It is frustrating because there is no tab completion for the namespace and I am hopeless at typing. So I decided to explore making a PowerShell Cmdlet to simplify this basic task. Cmdlets (read Command-Let) are just standard PowerShell commands like Get-Help, Get-Process etc. We can write our own Cmdlets using managed .NET code.

The Solution:

As this was my first Cmdlet I kept it painfully simple. I began by downloading the Visual Studio Powershell templates from channel 9. The templates were created by MS Architect Evangelist David Aiken. Check his blog for more info on creating PowerShell Cmdlets using the templates and other general Architecty/PowerShell stuff.

So the steps for creating the Cmdlet are as follows:

1.

After installing the templates create a new Visual Studio project, use the Windows PowerShell template (duh). This creates a SnapIn class that we will use as a vehicle to deliver the Cmdlet. In this case we only have the one Cmdlet but we could have a whole suite of them (more on that later). Now edit the automatically generated file with more friendly values. The only really important property is name. I have edited my name command to read:

public override string Name
{
get { return "SharePointCmdlets"; }
}

2.

Now add a new item to the project and use the Windows PowerShell PSCmdlet option. This will create a Cmdlet code stub to use. I have edited the verb and class name as required. The verb will be used as the prefix for the command. In my case I will reference the command in PowerShell with the syntax Get-SPSite.

[Cmdlet(VerbsCommon.Get, "SPSite", SupportsShouldProcess = true)]
public class GetSPSite : PSCmdlet

3.

Now I want to create a property to take some input from the console in the form of a parameter eg Get-PSSite http://test.

I have used the following code to achieve this:

private string _siteName;

[Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true, HelpMessage = "The URL of the site to return")]
[ValidateNotNullOrEmpty]
public string SiteName
{
get { return _siteName; }
set { _siteName = value; }
}

4.

Now for the actual meat. I have filled out the ProcessRecord method with code to get a reference to the SharePoint site and write it back to the PowerShell console.

protected override void ProcessRecord()
{
try
{
using (SPSite site = new SPSite(SiteName))
{
WriteObject(site);
}
}
catch (Exception ex)
{
WriteObject("ERROR: " + ex.Message);
}
}

5.

So thats it, compile it and its ready to go. However it needs to be registered for it actualy work in PowerShell. This is rather easy. Open a Visual Studio command prompt and navigate to the the bin\debug folder where the dll resides.

Run 'InstallUtil Name.dll' to make it available in PowerShell.

Then open up PowerShell an run the following command to register the SnapIn.

Add-PSSnapIn SharePointCmdlets

Now we can use our Cmdlet as demonstrated here:

Cmdlet

Note that you can now use tab completion to type the command as well.

To make things just that little bit easier I have added the snap in register command to my profile. This way it is regiestered automatically each time I open up PowerShell.

The profile script can be found at C:\WINDOWS\system32\windowspowershell\v1.0\profile.ps1. My profile script is pretty simple and looks like:

[System.Reflection.Assembly]::Load("Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c")

Add-PSSnapIn SharePointCmdlets

cd \

So this is loading the Microsoft.SharePoint dll, registering my Cmdlet and then setting the directory to the root.

Note that this is the global profile and will affect all users for the machine.

Next Steps:

So this was a trivial example I know, but it demonstrates the process and I believe it is a great starting point for bigger things. I would like to extend this simple example and add some more useful commands that return various stats about SharePoint sites. It could also include some basic administrative commands. Adding an installer is definatley on the cards. I would like to make this a community project (hosted on CodePlex) so that other SharePoint/Powershell enthusiasts can add useful Cmdlets and help make everyones life easier. So watch this space for more SharePoint Cmdlets in the near future.

Upcoming Wellington .NET User Group Session

On the 1st of August, I will be presenting a session on WSS development to the Wellington .NET User Group. Although we have a Wellington SharePoint User Group, it is focused on more high level MOSS topics. This session is intended to be a very technical, dev focused affair. The session description that I submitted goes along the lines of:

Developing with Windows SharePoint Services v3.0
- Delivered by Zac Smith of Provoke Solutions

With Windows SharePoint Services 3.0, IT professionals can tailor or extend the Windows SharePoint Services foundation to create new, efficient, Web-based tools and services specific to the organization, department, business process, or industry vertical. Through a highly customizable and extensible platform, companies can tie these new tools and services to existing line-of-business applications, capitalizing on existing IT investments and valuable information stored in back-end systems.

WSS is often thought of as just a document collaboration platform. This session will demonstrate how to use the Windows SharePoint Services platform to build rich, flexible, and scalable Web-based applications and Internet sites specific to the needs of your organization. As of version 3.0, WSS is now built on top of the ASP.NET 2.0 Framework. This allows modern .NET development techniques to be employed. With the major architectural improvements in version 3.0, WSS can now be realised as a powerful application framework.

The development topics being covered in this session may include the following:

  • Object Model Basics
  • Object Model Best Practices
  • Cross Site Queries (CAML)
  • SharePoint Designer Basics
  • WebPart Basics
  • WSS Event Handling
  • Microsoft and Community Developed Tools & Utilities
  • Features & the Solution Framework
  • WSS as an Application Framework

The session is intended to be at a technical level and there will be many code based demonstrations. It is targeted at competent .NET web developers. No in-depth WSS knowledge is required, however a basic understanding of SharePoint related technologies will be beneficial.

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.