Site Page Fundamentals

Chapter 2, “SharePoint Architecture,” introduced you to the key differences between application pages and site pages. You learned that application pages have an advantage over site pages in that they perform better and provide a developer with the ability to add in-line code. You also learned that site pages have some key advantages over application pages because they can be created dynamically and can also be customized by users on a site-by-site basis.

Chapter 2 discussed the role of the SPVirtualPathProvider component and introduced the principles of page ghosting and unghosting. As you remember, page ghosting is an optimization used with site pages in which a single page template can be used to process multiple page instances across many different sites. For example, the home page for every team site in a Microsoft Windows SharePoint Services (WSS) farm is based on an underlying page template named default.aspx that resides on the file system of the front-end Web server. A page template, such as default.aspx, is compiled into an assembly dynamic-link library (DLL) and loaded into memory just once per Web application. However, this page template and its efficient usage of memory can still be used to serve up pages for thousands of sites. This is an obvious advantage toward scalability.

When a user customizes a site page by using the SharePoint Designer and then saves those changes, a customized version of the page definition is stored in the content database. While this provides flexibility from a customization standpoint, it also can have a negative impact on performance and scalability. When the customized page is requested, its page definition must be retrieved from the Backend database server by the SPVirtualPathProvider component and then fed to the ASP.NET compiler, where it is parsed and loaded into memory. You can imagine that a Web application with thousands of customized pages requires more memory because each customized page definition must be separately parsed and loaded into memory within the application pool that is hosting the current Web application.

You should note that customized pages are not processed by using the standard ASP.NET model in which a page is compiled into an assembly DLL. Instead, customized pages are parsed by the ASP.NET page parser and then processed using the no-compile mode feature that was introduced with ASP.NET 2.0.

As a developer, your initial reaction to this might be to question why customized pages are processed in no-compile mode. Your instincts likely tell you that compiled pages run faster than no-compile pages. However, no-compile pages can be more efficient and more scalable in certain scenarios. This is especially true in a large WSS environment where the number of customized pages can reach into the thousands or tens of thousands.

No-compile pages can be loaded into memory and then unloaded in a manner that is not possible for compiled pages because the .NET Framework doesn’t really support the concept of unloading an assembly DLL from memory. The closest equivalent would be to recycle the current Windows process or the current .NET AppDomain. However, this type of recycling involves unloading all assembly DLLs from memory, not just those assembly DLLs that haven’t been used recently. Furthermore, the .NET Framework places an upper limit on the number of assembly DLLs that can be loaded into a .NET AppDomain.

No-compile pages provide higher levels of scalability because they do not require loading new assembly DLLs or managed classes into memory. Instead, the processing of no-compile pages involves loading control trees into memory. WSS can manage the memory usage for the control trees associated with customized pages more efficiently because they are not compiled into assembly DLLs. For example, once WSS has finished processing a customized page, it can unload the page’s control tree to free up memory for other purposes. Furthermore, nocompile pages eliminate the need to go through the compilation process, which actually provides faster response times for pages upon first access.

Programming with SPFile Objects

WSS tracks each site page as a file within the content database. You can access a site page through the WSS object model by using the SPFile object. For example, assume that you want to program against the home page for a site. You can obtain a reference to the required SPFile object by using the GetFile method of a SPWeb object.

 SPWeb site = SPContext.Current.Web; SPFile homePage = site.GetFile("default.aspx");

The SPFile class makes it possible to read and write to the contents of a site page. For example, the OpenBinary method of an SPFile object returns a binary array containing the page contents. The OpenBinaryStream method returns a System.IO.Stream object. Each of these methods provides an approach for reading the contents of a site page. An SPFile object also provides a SaveBinary method that allows you to update the contents of a site page as well. Note that updating the contents of a site page by using this method customizes the page and moves it into an customized or unghosted state.

The SPFile class provides several other methods for managing site pages within a site such as Delete, MoveTo, and CopyTo. The Delete method, as its name implies, removes the target file from the site. MoveTo makes it possible to move a file, such as a site page, to another location so that it’s accessible through a different URL. CopyTo allows you to clone a site page with a copy. Note that if you call CopyTo on an uncustomized page, it creates another uncustomized page. Likewise, if you call CopyTo on an customized page, it results in the creation of a customized page in an unghosted state.


Ghosted and uncustomized are terms used to describe site pages served up using file system templates. Unghosted and customized both refer to pages that exist entirely in the database, which no longer depend on a file system template.

The SPWeb object for a site also exposes a Files property with a public Add method that allows you to add new site pages. There is an overloaded version of the Add method that allows you to pass a stream object with the contents of the new page. The following example demonstrates writing the contents of a new page to a MemoryStream object and then using it to create a new site page named Hello.htm.

 // write out new page in memory stream MemoryStream stream = new MemoryStream(); StreamWriter writer = new StreamWriter(stream); writer.WriteLine("<html><body>"); writer.WriteLine("Hello, World"); writer.WriteLine("</body></html>"); writer.Flush(); // add new page to site SPWeb site = SPContext.Current.Web; site.Files.Add("hello.htm", stream);

Note that the Add method doesn’t support adding a new site page that is associated with an underlying page template. Therefore, site pages created by using the Add method are always created as customized pages in an unghosted state.

The SPFile class provides a CustomizedPageStatus property that makes it possible to determine whether a site page has been customized and placed in an unghosted state. The CustomizedPageStatus property is based on an enumeration type named SPCustomizedPageStatus. If a SPFile object for a site page has a CustomizedPageStatus property value of Uncustomized, it means that the page is still in a ghosted state. A site page with a CustomizedPageStatus property value of Customized has been customized and is in an unghosted state. The SPFile object also provides a method named RevertContentStream that removes any customizations and returns an unghosted page to its initial ghosted state.

SPFolder Objects

The files within a WSS site are structured within a hierarchy of folders. Each folder is represented in the WSS object model with an SPFolder object. Each SPFolder object contains a

Files property that allows you to enumerate through its files. If you want to enumerate through all of the files at the root folder of a site, you can access the RootFolder property of a SPWeb object and then use a foreach loop to enumerate through all of its files.

 SPWeb site = SPContext.Current.Web; SPFolder rootFolder = site.RootFolder; foreach (SPFile file in rootFolder.Files){   // process each file }

The WSS object model also makes it possible to enumerate through the folders within a folder. This, in turn, makes it possible to write code that enumerates through all of the folders within a site to discover all existing files. The following code displays an example of custom code that starts at the root folder of a site and uses recursion to populate an ASP.NET TreeView control.

 const string SITE_IMG = @"\_layouts\images\FPWEB16.GIF"; const string FOLDER_IMG = @"\_layouts\images\FOLDER16.GIF"; const string GHOSTED_FILE_IMG = @"\_layouts\images\NEWDOC.GIF"; const string UNGHOSTED_FILE_IMG = @"\_layouts\images\RAT16.GIF"; protected override void OnLoad(EventArgs e) {   SPWeb site = SPContext.Current.Web;   SPFolder rootFolder = site.RootFolder;   TreeNode rootNode = new TreeNode(site.Url, site.Url, SITE_IMG);   LoadFolderNodes(rootFolder, rootNode);   treeSiteFiles.Nodes.Add(rootNode);   treeSiteFiles.ExpandDepth = 1; } protected void LoadFolderNodes(SPFolder folder, TreeNode folderNode) {   foreach (SPFolder childFolder in folder.SubFolders) {     TreeNode childFolderNode = new TreeNode(childFolder.Name,                                             childFolder.Name,                                             FOLDER_IMG);     LoadFolderNodes(childFolder, childFolderNode);     folderNode.ChildNodes.Add(childFolderNode);   }   foreach (SPFile file in folder.Files) {     TreeNode fileNode;     if (file.CustomizedPageStatus == SPCustomizedPageStatus.Uncustomized) {       fileNode = new TreeNode(file.Name, file.Name, GHOSTED_FILE_IMG);     }     else {       fileNode = new TreeNode(file.Name, file.Name, UNGHOSTED_FILE_IMG);     }     folderNode.ChildNodes.Add(fileNode);   } }

Note that this code is also written to provide different images that allow the user to distinguish between pages that are customized and those that are uncustomized. A graphic of the resulting TreeView control is shown in Figure 3-1.

image from book
Figure 3-1: A WSS site contains a hierarchy of folders and files. Files such as .aspx and .htm pages can either be in an uncustomized or customized state.

Working with Page Templates

Up to this point, the discussion of page templates has revolved around using the standard page templates that are built into WSS. It is now time to explore how to create your own page templates and integrate them into a custom business solution. You can create and integrate custom page templates by using either a feature or a site definition. Because we have not yet discussed site definitions, this chapter focuses on the use of custom page templates within the context of a feature.

Examples of using page templates in this chapter are based on a Microsoft Visual Studio project named CustomSitePages that contains a feature of the same name. (The project is included on the companion Web site for this book.) Figure 3-2 displays the Solution Explorer window for this project. As you can see, the project contains a feature.xml and an elements.xml file like the other features that we built in earlier chapters. However, this feature also contains several .aspx files that are used to define site page templates, such as Page01.aspx and Page02.aspx. The CustomSitePages project also contains several ASP.NET user controls as well as the code for an ASP.NET custom control that will be discussed later in this chapter.

image from book
Figure 3-2: The CustomSitePages project demonstrates how to build a feature with custom site page templates.

If you open and build the CustomSitePages project, you find a post-build event that runs a batch file named Install.bat. This batch file copies the feature files along with the page templates into the proper location within the TEMPLATE directory and then installs the CustomSitePages feature by using the stsadm.exe command-line utility. Note that the CustomSitePages feature is designed to activate within the context of a site. After the feature is installed, you can activate it within any site in the current farm and follow along with these examples.

When the CustomSitePages feature is activated, it contains declarative logic in elements.xml to provision site page instances from its page templates. The code in the FeatureActivated event extends the navigation components of a WSS site by adding two new drop-down menus to the top link bar with menu items to navigate to the newly provisioned site page instance. The technique for adding these drop-down menus to the top link bar is explained later in this chapter.

Let’s start with a simple definition for a page template. Examine the following definition for the page template named Page01.aspx.

  <%@ Page MasterPageFile="~masterurl/default.master"     meta:prog %> <asp:Content runat="server" ContentPlaceHolder>    <h3>Hello World</h3>    A simple page template used to create site pages </asp:Content> 

The Page directive at the top of this page template assigns a value of~masterurl/default .master to the MasterPageFile attribute to link to the standard master page used by site pages within WSS sites. We will defer a more detailed discussion of master pages and the MasterPageFile attribute until later in this chapter. For now, simply assume that this site page template is designed to link to the standard master page.

You should also notice that the Page directive in the previous example contains a meta:progid attribute with a value of SharePoint.WebPartPage.Document. This attribute is included to make that page compatible with the SharePoint Designer, and is also available in the SPFile object’s ProgID property. Once site page instances have been provisioned by using this page template, users can open these pages with the SharePoint Designer and customize their content.

This simple example demonstrates the power and elegance of master pages in WSS development. You should be impressed at how little text is needed to define a simple page template. All that’s really required for a simple page template is to link to a master page and supply some unique content for the placeholder named PlaceHolderMain. As you learn more about how WSS uses master pages, you will discover that there are many more named placeholders that you can optionally override within your page templates to enrich them with all types of content such as controls, scripts, and styles.

Now that you’ve seen how to create a simple page template, it’s time to put it to use. Keep in mind that a page template, such as Page01.aspx, serves no purpose until you begin using it to provision site page instances. This can be done by creating a feature that contains a special type of element known as a Module.

A Module element can be thought of as a file set. When you create a Module, you add one or more inner File elements. The key point is that each File element is used to provision an instance of a file from a file template. Remember that the file template exists on the file system of the front-end Web server, whereas the file instance being provisioned is being created inside the context of a particular site.

In this particular case, we want to provision an instance of a site page from the page template named Page01.aspx. Note that the top-level directory of the CustomSitePages feature contains a nested directory named PageTemplates that contains all of the page templates. When you define a Module element, you can specify a Path attribute that points to a source directory, such as PageTemplates. You can also specify a Url element if you would like to instantiate the resulting site page instance within an inner folder instead of at the root folder of the target site.

 <Elements xmlns="">   <Module Path="PageTemplates" Url="SitePages" >     <File Url="Page01.aspx" Type="Ghostable" />   </Module> </Elements>

Note that the File element within this example is created with a Url attribute that points to the source file for the page template. When you activate a feature that contains this Module element, WSS provisions a site page instance within the target site at the following relative path.


The user can navigate to this page by using the Site Pages drop-down menu and clicking on the menu item with a caption of Site Page 1. Figure 3-3 depicts the resulting site page instance.

image from book
Figure 3-3: A Module element allows you to provision a site page instance from page templates.

Note that the File element in the previous example contains a Type attribute with a value of Ghostable. When a site page instance, such as Page01.aspx, is provisioned, it initially exists in an uncustomized state and benefits from the principles of page ghosting. This means that you can activate this feature in a thousand different sites within a Web application and that all sites use a single compiled version of the page. Page ghosting also makes it possible to make changes to the page template on the file system of the front-end Web server and have those changes affect all of the sites that have pages provisioned from this page template.

Only two possible settings exist for the Type attribute: Ghostable and GhostableInLibrary. These two settings are used to differentiate between files that are provisioned inside a document library and those that are not. In this case, the site page instance has a Type of Ghostable because it is not being provisioned inside a document library. Later in the chapter, you will encounter an example of a File element whose Type attribute value will be defined as GhostableInLibrary.

You should also note that when defining a File element, you can optionally include the Name element. This makes it possible to provision a site page instance with a name that differs from the name of the underlying page template. This technique wasn’t used in the previous example, so the resulting site page instance was provisioned with the same name as the page template. However, you can extend the Module element shown earlier to provision several different site page instances from a single page template and give them all different names.

 <Elements xmlns="">   <Module Path="PageTemplates" Url="SitePages" >     <File Url="Page01.aspx" Name="PageA.aspx Type="Ghostable" />     <File Url="Page01.aspx" Name="PageB.aspx Type="Ghostable" />     <File Url="Page01.aspx" Name="PageC.aspx Type="Ghostable" />   </Module> </Elements>

Safe Mode Processing

It’s important to understand that all customized site pages are parsed and processed in a special mode known as safe mode. The primary motivation for safe mode involves the fact that standard users can modify the contents of site pages. In other words, a user (such as a site owner) possessing no administrator privileges within the farm can make any modifications to a page within a site. Consider a scenario in a large farm in which a site administrator attempts to mount an attack on the Web server by writing C# code within a customized site page inside an in-line script block. Safe mode prevents this type of attack by disallowing in-line script in any customized source.

Examine the code in the page template named Page02.aspx. It contains a simple in-line script to write a message back to the browser.

 <%@ Page Language="C#" MasterPageFile="~masterurl/default.master"     meta:prog %> <asp:Content  runat="server"              ContentPlaceHolder>   <h3>Page 2</h3>   <% Response.Write("Hello world from server-side script!"); %> </asp:Content>

Note that this page and the in-line script run just fine as long as the page remains uncustomized in a ghosted state. Remember that WSS compiles a ghosted page into an assembly DLL for processing. However, as soon as a user modifies any aspect of this page with the SharePoint Designer and moves the site page into an unghosted state, WSS then begins to use safe mode to process it. Because the page contains in-line script, WSS refuses to process it in safe mode and generates the error message shown in Figure 3-4.

image from book
Figure 3-4: Customized pages run in safe mode and cannot contain in-line script.

You obviously don’t want your users to experience error messages like the one shown in Figure 3-4. For this reason, you should avoid adding in-line script to page templates. Following this guideline helps to eliminate scenarios in which pages mysteriously stop working after they are edited by users with the SharePoint Designer.

In rare cases, you might decide to turn down or turn off the protection afforded by safe mode. In this situation, you can add an entry to the web.config file of the hosting Web application to instruct WSS to change the behavior of safe mode processing. For example, assume that you want to allow in-line scripts for site pages inside the SitePages folder in a site at the path of /sites/Sales. You can accomplish this by adding the following PageParserPath element within the SharePoint section of the web.config file.

 <SharePoint>   <SafeMode ... >     <PageParserPaths>       <PageParserPath           VirtualPath="/sites/Sales/SitePages/*"           IncludeSubFolders="true"           CompilationMode="Always"           AllowServerSideScript="true" />     </PageParserPaths>   </SafeMode> </SharePoint>

If you examine the PageParserPath element, you see that the VirtualPath attribute has a Web application relative path followed by an asterisk, which includes every site page in that particular folder. Also note that the CompilationMode attribute has a value of Always and the AllowServerSideScript attribute has a value of true. This instructs the safe mode parser to compile all site pages into assembly DLLs and allow in-line script.

Note that a page must be compiled into an assembly DLL to support in-line script, which means that it is not valid to assign a value of Never to the CompilationMode attribute while assigning a value of true to the AllowServerSideScript attribute. Also note that you can assign a value of Auto instead of a value of Always to the CompilationMode attribute. This has the effect of compiling only pages that contain in-line script. When the CompilationMode attribute has a value of Auto, pages without in-line script are still run in no-compile mode.

It is possible to enable in-line script for all site pages within a Web application by configuring the VirtualPath attribute with a value of /* and then setting the CompilationMode attribute to a value of Always or Auto. However, two significant factors should motivate you not to do this.

The first factor is security. By enabling in-line script for all site pages within a Web application, you open the door to attacks on the Web server because any user who has the ability to customize a page can freely write managed code that executes on the Web server.

The second factor pertains to scalability. Earlier in this chapter, I discussed how no-compile pages are more scalable than compiled pages in a large Web application. WSS experiences scaling problems if your Web application attempts to compile and load thousands of assembly DLLs for all of your customized pages. At the very least, you should prefer a CompilationMode setting of Auto instead of Always so that only pages that actually contain script are compiled into assembly DLLs, whereas those pages that do not contain script continue to be parsed and processed in no-compile mode.

Safe Controls

Safe mode processing goes a step beyond protecting against in-line script by also considering what controls a user might place on a customized page. For example, imagine a scenario in which a site administrator tries to mount an attack by adding a server-side control to a site page and parameterizing it in a certain way. Safe mode allows the farm administrator to determine which controls can be used in pages that are processed in safe mode.

Customized pages can only contain server-side controls that are explicitly registered as safe controls. Registering a control as a safe control is accomplished by adding a SafeControl entry into the web.config file for the hosting Web application.

 <SafeControls>   <SafeControl     Assembly="Microsoft.SharePoint, …"     Namespace="Microsoft.SharePoint.WebControls"     TypeName="*"     AllowRemoteDesigner="True" /> </SafeControls>

Note that the standard web.config file for a Web application automatically includes SafeControl entries for the standard server-side controls and Web Parts included with ASP.NET and WSS. In the next section, you will learn how to add a SafeControl entry that is required to place a custom server-side control on a customized page. In Chapter 4, “Web Parts,” we will revisit the topic of safe controls and discuss how they pertain to custom Web Part deployment.

Note that a PageParserPath element, in addition to allowing in-line script, can also override the default safe mode behavior and allow for server-side controls that are explicitly registered as safe. For example, you can allow the users of a particular site to add any server-side controls to customized pages by using the following entry within the web.config file.

 <SharePoint>   <SafeMode ... >     <PageParserPaths>       <PageParserPath           VirtualPath="/sites/Sales/*"           AllowUnsafeControls="true" />     </PageParserPaths>   </SafeMode> </SharePoint>

Note that using this option affects only which server-side controls can be added to a page when customizing a page with a tool, such as the SharePoint Designer. This configuration option does not extend to control instances when users are adding Web Parts to Web Part zones on a page through the browser. Assembly DLLs containing Web Parts must always be explicitly registered by using SafeControl elements for users to be able to place them inside Web Part zones.

Although you have just learned several ways to disable safe mode or lessen its effects, you should remember to proceed here with extreme caution. It’s usually best to leave safe mode with its default behavior. WSS was engineered with safe mode processing to protect the farm from attacks and allow WSS to scale out the way it was designed in large farm environments.

Inside Microsoft Windows Sharepoint Services Version 3
Inside Microsoft Windows Sharepoint Services Version 3
ISBN: 735623201
Year: 2007
Pages: 92 © 2008-2017.
If you may any questions please contact us: