Customizing AccessDenied, SignOut and other pages in SharePoint 2010 using PowerShell

When building a custom solution in SharePoint, whether intranet or public facing, you often need to balance out-of-the-box functionality with the custom look and feel your customer is asking for.
Page Content


When building a custom solution in SharePoint, whether intranet or public facing, you often need to balance out-of-the-box functionality with the custom look and feel your customer is asking for.

There are number of out-of-the-box system pages which SharePoint uses to display upon various events. For example; AccessDenied.aspx page is displayed when a user is trying to access the resource they don’t have permission to. Signout.aspx is a common one – displayed when user chooses to sign out.

A few others include: Confirmation.aspx Error.aspx Login.aspx ReqAcc.aspx WebDeleted.aspx

In SharePoint 2010 there is an elegant way to customize what user’s will see without changing the original files. Here are the steps:

1. First you can copy an existing page from the Template/Layouts, in our case we’ll use AccessDenied.aspx.

You can give it any name. If you’re using a Visual Studio solution simply add this new file to your solution structure under Layouts mapped folder.

2. Make any desired changes to the newly created page. If you made a copy of an existing page, remember that it’s referencing the original assembly and you still need to follow the structure outlined in the page placeholders to avoid the page erroring out.

Alternately, you can create your own page in VS2010 which can reference its own assembly.

3. Assuming the page has been deployed to the layouts folder. Execute the following PowerShell to set it as a default page for the role, in our case AccessDenied page role.

1 PS C:\Users\Administrator> $site = get-spsite ""
2 PS C:\Users\Administrator> $webApp = $site.WebApplication
3 PS C:\Users\Administrator> $webapp.UpdateMappedPage(1, "/_layouts/AccessDeniedNew.aspx")
4 True
5 PS C:\Users\Administrator> $webapp.Update()
6 PS C:\Users\Administrator>

In here, the value of “1″ in UpdateMappedPage, specifies a reference to an enumeration value for other page roles available, see SPCustomPage for more info.

4. We’re set. Since we’re testing AccessDenied page, I’m going to log in as a reader and access “Site collection administrators” page by URL.

As a result I am getting my customized AccessDenied page:


Notice that the URL of the page still remains the same AccessDenied.aspx and not our AccessDeniedNew.aspx – however, the content is different.

5. To reverse the customization, execute:

1 PS C:\Users\Administrator> $webapp.UpdateMappedPage(1, $null)
2 True
3 PS C:\Users\Administrator> $webapp.Update()
4 PS C:\Users\Administrator>

NOTE: The custom page must always be hosted in _layouts, otherwise the UpdateMappedPage will fail. If you need to show your content page as a result – you can execute a redirect from the custom page in _layouts to your content page.


How to Add a Custom Visual Studio Workflow to SharePoint Designer

This article explains, step by step, how to add a custom Visual Studio work flow to SharePoint designer in SharePoint 2010 using Visual Studio 2010.
Page Content

This article explains, step by step, how to add a custom Visual Studio work flow to SharePoint designer in SharePoint 2010 using Visual Studio 2010.

Create an Empty Sequence Work Flow project.


Select .Net Framework 3.5 and select the Sequential Workflow Project.


In this example we are going to add a custom string functionality to SharePoint designer. After creating the project you will get a blank activity as follows.


Delete that activity and add the Activity item to the project as shown above. I named it subStringAfterText.cs


You will then get a blank Sequence Activity.

Add references to the Project

You will then need to add Microsoft.SharePoint.dll and Microsoft.SharePoint.WorkflowActions.dll to the project. Those dlls can be found in the following paths.

  • C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.dll
  • C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.WorkflowActions.dll


My task is to develop a simple string function that will return the text after the search text. This is very simple and you can use this method to develop any Custom SPD workflows.

001 using System;
002 using System.ComponentModel;
003 using System.ComponentModel.Design;
004 using System.Collections;
005 using System.Linq;
006 using System.Workflow.ComponentModel;
007 using System.Workflow.ComponentModel.Design;
008 using System.Workflow.ComponentModel.Compiler;
009 using System.Workflow.ComponentModel.Serialization;
010 using System.Workflow.Runtime;
011 using System.Workflow.Activities;
012 using System.Workflow.Activities.Rules;
013 using System.Diagnostics;
014 using Microsoft.SharePoint;
015 using Microsoft.SharePoint.WorkflowActions;
016 using Microsoft.SharePoint.Workflow;
018 namespace getSubStringAfter
019 {
020 public partial class subStringAfterText : SequenceActivity
021 {
022 // Declare event log
023 private EventLog eventLog;
026 // Declare dependency propoerties to get and set values to the activity
028 public static DependencyProperty SourceStringProperty = System.Workflow.ComponentModel.DependencyProperty.Register("SourceString", typeof(string), typeof(subStringAfterText));
029 public static DependencyProperty SearchStringProperty = System.Workflow.ComponentModel.DependencyProperty.Register("SearchString", typeof(string), typeof(subStringAfterText));
030 public static DependencyProperty FoundStringProperty = System.Workflow.ComponentModel.DependencyProperty.Register("FoundString", typeof(string), typeof(subStringAfterText));
031 // Specify validations to validate inputs
032 [Description("")]
033 [ValidationOption(ValidationOption.Required)]
034 [Browsable(true)]
035 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
036 public string SourceString
037 {
038 get
039 {
040 return (string)base.GetValue(SourceStringProperty);
041 }
042 set
043 {
044 base.SetValue(SourceStringProperty, value);
045 }
046 }
048 [Description("")]
049 [ValidationOption(ValidationOption.Required)]
050 [Browsable(true)]
051 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
052 public string SearchString
053 {
054 get
055 {
056 return (string)base.GetValue(SearchStringProperty);
057 }
058 set
059 {
060 base.SetValue(SearchStringProperty, value);
061 }
062 }
064 [Browsable(true)]
065 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
066 public string FoundString
067 {
068 get
069 {
070 return (string)base.GetValue(FoundStringProperty);
071 }
072 set
073 {
074 base.SetValue(FoundStringProperty, value);
075 }
076 }
078 //The SharePoint workflow properties activation object
079 public static DependencyProperty __ActivationPropertiesProperty = DependencyProperty.Register("__ActivationProperties", typeof(SPWorkflowActivationProperties), typeof(subStringAfterText));
081 // Using a DependencyProperty as the backing store for WorkflowInstanceIdProperty. This enables animation, styling, binding, etc...
082 public static DependencyProperty WorkflowInstanceIdVariableProperty = DependencyProperty.Register("WorkflowInstanceIdVariable", typeof(string), typeof(subStringAfterText));
084 // Using a DependencyProperty as the backing store for WorkflowInstanceIdProperty. This enables animation, styling, binding, etc...
085 public static DependencyProperty __ContextProperty = DependencyProperty.Register("__Context", typeof(WorkflowContext), typeof(subStringAfterText));
087 [Description("The WorkflowInstanceId for the workflow.")]
088 [ValidationOption(ValidationOption.Required)]
089 public string WorkflowInstanceIdVariable
090 {
091 get { return (string)(base.GetValue(WorkflowInstanceIdVariableProperty)); }
092 set { base.SetValue(WorkflowInstanceIdVariableProperty, value); }
093 }
095 [Description("The Workflow Properties")]
096 [ValidationOption(ValidationOption.Required)]
097 public SPWorkflowActivationProperties __ActivationProperties
098 {
099 get { return (SPWorkflowActivationProperties)(base.GetValue(__ActivationPropertiesProperty)); }
100 set { base.SetValue(__ActivationPropertiesProperty, value); }
101 }
103 [Description("The Workflow Context object")]
104 [ValidationOption(ValidationOption.Optional)]
105 [Browsable(false)]
106 [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
107 public WorkflowContext __Context
108 {
109 get
110 {
111 return (WorkflowContext)(base.GetValue(__ContextProperty));
112 }
114 set
115 {
116 base.SetValue(__ContextProperty, value);
117 }
118 }
120 // Override the activity execution
121 protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
123 //Set up the Event Logging object 
124 eventLog = new EventLog("Workflow");
125 eventLog.Source = "SharePoint Workflow";
127 try
128 {
129 // Your Method
130 getTheString();
131 }
132 finally
133 {
134 //Dispose of the Event Logging object 
135 eventLog.Dispose();
136 }
138 //Indicate the activity has closed 
139 return ActivityExecutionStatus.Closed;
140 }
143 private void getTheString()
144 {
145 this.FoundString = SourceString.Substring(SourceString.IndexOf(SearchString) + SearchString.Length -1);
146 }
148 }
149 }

Code Explanation

Dependency properties are used to exchange data from a workflow instance. Therefore the following properties needed to exchange primary data.

//The SharePoint workflow properties activation object // Using a DependencyProperty as the backing store for WorkflowInstanceIdProperty. This enables animation, styling, binding, etc…

public static DependencyProperty __ActivationPropertiesProperty = DependencyProperty.Register(“__ActivationProperties”, typeof(SPWorkflowActivationProperties), typeof(subStringAfterText));

public static DependencyProperty WorkflowInstanceIdVariableProperty = DependencyProperty.Register(“WorkflowInstanceIdVariable”, typeof(string), typeof(subStringAfterText));

// Using a DependencyProperty as the backing store for WorkflowInstanceIdProperty. This enables animation, styling, binding, etc… public static DependencyProperty __ContextProperty = DependencyProperty.Register(“__Context”, typeof(WorkflowContext), typeof(subStringAfterText));

//These properties should have getter setter methods. attributes define the visibility , validation and etc.. Ex :-

[Description(“The WorkflowInstanceId for the workflow.”)] [ValidationOption(ValidationOption.Required)] public string WorkflowInstanceIdVariable { get { return (string)(base.GetValue(WorkflowInstanceIdVariableProperty)); } set { base.SetValue(WorkflowInstanceIdVariableProperty, value); } }

// Then you need your own properties and getters setters

public staticDependencyProperty SourceStringProperty = System.Workflow.ComponentModel.DependencyProperty.Register(“SourceString”,typeof(string),typeof(subStringAfterText));

public static DependencyPropertySearchStringProperty = System.Workflow.ComponentModel.DependencyProperty.Register(“SearchString”,typeof(string),typeof(subStringAfterText));

public static DependencyPropertyFoundStringProperty = System.Workflow.ComponentModel.DependencyProperty.Register(“FoundString”,typeof(string),typeof(subStringAfterText));

// Override the activity execution protected overrideActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { //Set up the Event Logging object eventLog =new EventLog(“Workflow”); eventLog.Source =”SharePoint Workflow”;
try { // Your Method getTheString(); } finally { //Dispose of the Event Logging object eventLog.Dispose(); }
//Indicate the activity has closed
returnActivityExecutionStatus.Closed; }

Sign the application

Sign the application using a strong key. This way you can go to project properties and sign the application as follows.



Build your application and place the assembly to the GAC. In here there are are two GAC maintaining with framework 4.0

  • C:\Windows\Microsoft.NET\assembly
  • C:\Windows\assembly

To work the assembly, this should be registered under “C:\Windows\assembly” if you use Target Framework as 4.0 then it will register the assembly in C:\Windows\Microsoft.NET\assembly. Therefore use target framework as 3.5

Register Assembly Under “C:\Windows\assembly”


Make sure the target framework is 3.5 of the project. if not it will get add to the location “C:\Windows\assembly

For that

  • Go to the Visual Studio Command Prompt as administrator
  • Go to your bin/debug folder (place where the dll is build)
  • Type gacutil /i getSubStringAfter.dll

If you did it correctly you can see your assembly in the GAC. To view the GAC you can type “C:\Windows\assembly” in the Run Window.


Creating work flow template actions file.

You then need to create an .ACTIONS file. This is the file that is used by SPD to get the workflow information. Therefore create a file named getSubStringAfter.ACTIONS and put the following code there. you need to put the correct public key token, namespace and class name.


01 <?xml version="1.0" encoding="utf-8"?>
02 <WorkflowInfo Language="en-us" >
03    <Actions Sequential="then" Parallel="and">
04      <Action Name="get the substring after the text"
05          ClassName="getSubStringAfter.subStringAfterText"
06          Assembly="getSubStringAfter, Version=, Culture=neutral, PublicKeyToken=d98a44afbf8ccb86"
07          AppliesTo="all"
08          Category="Custom String Actions">
09        <RuleDesigner Sentence="Search %1 from %2 and store in %3.">
10          <FieldBind Field="SearchString" Text="this text" DesignerType="stringbuilder" Id="1"/>
11          <FieldBind Field="SourceString" Text="this address" Id="2" DesignerType="stringbuilder" />
12          <FieldBind Field="FoundString" Text="variable" Id="3" DesignerType="ParameterNames" />
13        </RuleDesigner>
14        <Parameters>
15          <Parameter Name="SearchString" Type="System.String, mscorlib" Direction="In" />
16          <Parameter Name="SourceString" Type="System.String, mscorlib" Direction="In" />
17          <Parameter Name="FoundString" Type="System.String, mscorlib" Direction="Out" />
18        </Parameters>
19      </Action>
20    </Actions>
21 </WorkflowInfo>

then copy this file and put in to the following SharePoint server location C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\1033\Workflow

Modify the web Config

Modify the web.config of your site. You’ll need to put SafeControl and authorizedType in here. Also please make sure public key token , namespace , version and class is correct otherwise it will not work. For example it may not show in SPD, or it will not get added to the work flow step even though it showed in SPD.


<authorizedTypeAssembly=”getSubStringAfter, Version=, Culture=neutral, PublicKeyToken=d98a44afbf8ccb86″Namespace=”getSubStringAfter”TypeName=”subStringAfterText”Authorized=”True”/> <SafeControlAssembly=”getSubStringAfter, Version=, Culture=neutral, PublicKeyToken=d98a44afbf8ccb86″Namespace=”getSubStringAfter”TypeName=”*”Safe=”True”/>

Finally ..

  • reset the IIS
  • Now open SPD


I took nearly one week to figure this out. Anyway, finally I’ve got a solution. 🙂