If you have worked a lot with MOSS you probably know how to make new
page layouts. But if you create new page layouts you might sometimes
wonder that how could I add some common functionalities to my page
layout pages. One example could be localization. You have decided that
Variations isn't the way to go in your case, but you still want to have
different site structures for different languages... and of course you
want to have texts localized. Or you want to change your master page for
some reason on the fly... one example could be for printing reasons. Or
even wilder... you want to change you page layout to another! You could
do this kind of stuff pretty easily if you create your own
PublishingLayoutPage class that
has support your new functionalities. I'm going to explain how you can
do that with SharePoint Designer and Visual Studio.
Create new class that will extend the functionality of PublishingLayoutPage
I
started my journey by creating new Class Library project. I named it
"Microsoft.MCS.Common" (since I work in MCS inside Microsoft... cool
naming right :-). I added new class and named it
PublishingLayoutPageEx. I inherited that from
PublishingLayoutPage which is class behind page layouts. Where did I got that class name? Well I just opened
ArticleLeft.aspx with SharePoint Designer and checked the first line:
<%
@ Page language="C#" Inherits="Microsoft.SharePoint.Publishing.PublishingLayoutPage,
Microsoft.SharePoint.Publishing, Version=12.0.0.0,Culture=neutral,
PublicKeyToken=71e9bce111e9429c" %>
So it was pretty obvious that if I want to extend the functionality of the basic publishing page, I needed to inherit from it.
At this point my code looked like this (not much since we just started):
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Web.UI;
5 using System.Globalization;
6 using System.Threading;
7 using Microsoft.SharePoint.Publishing;
8 using Microsoft.SharePoint;
9
10 namespace Microsoft.MCS.Common
11 {
12 public class PublishingLayoutPageEx : PublishingLayoutPage
13 {
14 public PublishingLayoutPageEx()
15 : base()
16 {
17 }
18 }
19 }
And
now I'm ready to test my new class in action. I just added strong name
key, compiled and put it in the GAC. And then I changed the
ArticleLeft.aspx to use my new class:
<%
@ Page language="C#" Inherits="Microsoft.MCS.Common.PublishingLayoutPageEx,
Microsoft.MCS.Common,Version=1.0.0.0,
Culture=neutral,PublicKeyToken=b1e9400215c03709" %>
<small sidetrack to .NET Reflector>
If you're interestested in the stuff that's implemented in
PublishingLayoutPage, then you can play around with incredible tool: Lutz Roeder's
NET Reflector:
In just few clicks we can see that there is some
MasterPageFile retrieving in
OnPreInit:
![](https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_sWrh7bWYGxMiebdVpAXY3deY6l6BatAo1zTSBBSRKQRClp6UMYT5OXn-9ujMgIaNhFBaHYsd1pEoFU64v8oKO7Ly0iwa8psnBsal0XfoqfmWnExqZcpVppZ_iRwe0ngSL53PFcWNh-Kw=s0-d)
</small sidetrack to .NET Reflector>
If you now try your new
PublishingLayoutPageEx in action you'll get this kind of error message:
Server Error in '/' Application.
Parser Error
Description: An
error occurred during the parsing of a resource required to service
this request. Please review the following specific parse error details
and modify your source file appropriately.
Parser Error Message: The base type 'Microsoft.MCS.Common.PublishingLayoutPageEx' is not allowed for this page. The type is not registered as safe.
Source Error:
<%@ Page language="C#" Inherits="Microsoft.MCS.Common.PublishingLayoutPageEx,Microsoft.MCS.Common,Version=1.0.0.0,Culture=neutral,PublicKeyToken=b1e9400215c03709" %>
<%@ Register Tagprefix="SharePointWebControls" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="PublishingNavigation" Namespace="Microsoft.SharePoint.Publishing.Navigation" Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ContentPlaceholderID="PlaceHolderPageTitle" runat="server">
|
Source File: /_catalogs/masterpage/ArticleLeft.aspx Line: 1
Version Information: Microsoft .NET Framework Version:2.0.50727.42; ASP.NET Version:2.0.50727.210
That only means that we need to mark that component as Safe so that SharePoint will load it. Let's just modify our applications web.config file by adding following line in there:
<SafeControl Assembly="Microsoft.MCS.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=b1e9400215c03709" Namespace="Microsoft.MCS.Common" TypeName="PublishingLayoutPageEx" Safe="True" AllowRemoteDesigner="true" />
And then hit F5 in your browser and you should be all set. Now you have base what we're going to extend in next.
Add localization support to your pages
If you haven't played with ASP.NET Resource files, then you should take small detour into
www.asp.net localization quickstart.
So now you know about .RESX files :-) I created
Example.resx,
Example.en-US.resx and
Example.fi-FI. I have added only two words to the resource files:
- House:
- You:
I copied those resource files to my applications
App_GlobalResouces folder:
C:\Inetpub\wwwroot\wss\VirtualDirectories\80\App_GlobalResources
Now I modified my
default_Janne.master page so that it would receive text from my resource files. I added following line just before
</body> in master page.
<asp:Literal ID="house" runat="server" Text="<%$Resources:Example,House%>" /> <->
<asp:Literal ID="you" runat="server" Text="<%$Resources:Example,You%>" />
We have now added resource files and modified master page so that
it will take text from our resource file. Let's just add code to our
new class so that we could change the language on the fly.
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Web.UI;
5 using System.Globalization;
6 using System.Threading;
7 using Microsoft.SharePoint.Publishing;
8 using Microsoft.SharePoint;
9
10 namespace Microsoft.MCS.Common
11 {
12 public class PublishingLayoutPageEx : PublishingLayoutPage
13 {
14 public PublishingLayoutPageEx()
15 : base()
16 {
17 }
18
19 protected override void OnPreInit()
20 {
21 base.OnPreInit();
22 this.InitializeCulture();
23 }
24
25 protected override void InitializeCulture()
26 {
27 if (Request["mylang"] != null)
28 {
29 Thread.CurrentThread.CurrentCulture = new CultureInfo(Request"mylang"].ToString());
30 Thread.CurrentThread.CurrentUICulture = new CultureInfo(Request["mylang"].ToString());
31 }
32
33 base.InitializeCulture();
34 }
35 }
36 }
And now we can change the language from URL:
Here is result without the
mylang parameter:
![](https://lh3.googleusercontent.com/blogger_img_proxy/AEn0k_up2Qa6Ha-UNPBUq4qEviERXtqVymWmLITMpq7sN5IzT12sxH_hRd_BCBRvce8fU3P2YTj_1_ktrrIocP0MLOR6g_wZVcIQGFL2mogx9Xdmyxkb8sIlB7TLCo-j2v4CX6XcLcUKeIU0GQ=s0-d)
Of course you might not want to change your language by url parameter
:-) This is just sample that you CAN do that. Maybe it would be much
wiser to use some kind of site structure for localization. But I'll
leave that to you...
Change master page on the fly
Now we want to make something fancier... like changing the master
page on the fly. You could want to use this for print layouts, smaller
screen, mobile etc. But anyway.. You just might want to do that
sometimes :-)
So let's throw some code in here and see what happens:
...
1 protected override void OnPreInit()
2 {
3 base.OnPreInit();
4 if (Request["Print"] != null)
5 {
6 this.MasterPageFile = "/_catalogs/masterpage/BlueBand.master";
7 }
8 }
...
On lines 4 to 6 we have just check that if there is mysterious
Print parameter set. If that is set, we'll change the master page to nicely hardcode one. Let's see what happens on our browser:
So the result is quite easy to see... our master page changed from
default_janne.master to
BlueBand.master.
Change the page layout
Before I start... I'm going to give credit of this idea to
Vesa Juvonen
(colleague of mine at MCS who also works with SharePoint). He said that
this would be interesting thing to checkout. And since I happened to
have some code ready we tried this stuff on my environment. But he's
going to create full solution of this page layout change and publish it
in his
blog. So you probably want to check that place out too. Okay.. but let's get back to the subject.
This might sound a bit strange but still... sometimes you might want
to change page layout after the page has been created. Consider the
Article Page content type which is OOB content type in SharePoint. It has 4 different kind of page layouts. User could have selected
Image on right
layout and has filled the page with data. After a while you want to
change it to another layout.... BUT there isn't easy way to do that...
unless we'll extend our nice class again.
Idea is take (again) some nice url parameter that tells the
destination page layout. In this example I'll just take integer which is
used to get the correct page layout from array of possible page layouts
of this content type. And yes... I know that this code sample has a lot
to improve... It just gives you ideas.
Let's throw some code in here and see what happens:
...
1 protected override void OnPreInit()
2 {
3 SPContext current = SPContext.Current;
4 if (current != null &&
5 Request["changepagelayout"] != null &&
6 Request["done"] == null)
7 {
8 SPWeb web = current.Web;
9 // We need to allow unsafe updates in order to do this: 10 web.AllowUnsafeUpdates = true;
11 web.Update();
12
13 PublishingWeb publishingWeb = PublishingWeb.GetPublishingWeb(web);
14 PageLayout[] layouts = publishingWeb.GetAvailablePageLayouts(current.ListItem.ContentType.Parent.Id);
15 PublishingPage publishingPage = PublishingPage.GetPublishingPage(current.ListItem);
16 publishingPage.CheckOut();
17 // This is the magic:18 publishingPage.Layout = layouts[Convert.ToInt32(Request["changepagelayout"])];
19 publishingPage.Update();
20 publishingPage.CheckIn("We have changed page layout");
21
22 SPFile file = current.ListItem.File;
23 file.Publish("Publishing after page layout change");
24 // We have content approval on: 25 file.Approve("Approving the page layout change");
26 Response.Redirect(Request.Url + "&done=true");
27 }
28 base.OnPreInit(e);
29 }
...
And you can right away see from code that there isn't any checks or
any error handling. So this code is only for demonstration purposes and
you shouldn't take it any other way.... but here we can see the results
after user has type in parameter
changepagelayout=1 (Image on left):
And here is page if parameter is
2 (Image on right)
.
If you look at the code on line 14 where page layouts are retrieved... I'm using
Parent
of the current content type. You might ask why... But the reason is
simple since your content type from the list actually inherits the
Article Page content type from Site collection level. So if you would use
ListItem.ContentType you wouldn't get those 4 page layouts of
Article Page.
Insted you need to get the parent of the content type in the list and
then you get 4 different page layouts. Makes sense if you think how
inheritance in SharePoint works.
If you wonder that
done parameter I'm using... It is just helper to avoid recursive page layout change ;-)
Note: If you look at the code you probably already
noticed that it's not changing the layout for this rendering... it has
changed the page layout permanently. Of course you can change it back if
you want to.
Summary
You can use a lot of stuff from ASP.NET right in your SharePoint page
layouts. I can't even imagine all the capabilities of this but I'm just
going to give you brief list what I can think of now:
1) Localization:
- This one is obvious :-) I live in Finland and we need to deal with this in every project.
2) Change the master page
3) Change page layout
- If you had created page but you later on want to change to more suitable one... here's how you can do it
I hope you got the idea of this post. I know I could improve those
samples a lot, but I just wanted to share my idea and give you the
opportunity to make it much better than I did.
Anyways... happy hacking!