There are two kinds
of pages that are available in SharePoint. The first are application
pages that are read from the disk while the second are site pages that
are read from the content database. Whenever SharePoint gets an HTTP
request, it uses an ASP.NET concept called as the VirtualPathProvider to
fetch the actual contents of the page. The VirtualPathProvider class in
ASP.NET provides a set of methods that enable a web application to
retrieve resources from a virtual file system. SharePoint has one such
class located at
Microsoft.SharePoint.ApplicationRuntime.SPVirtualPathProvider.
Let me take you into a quick dive into the innards of SharePoint. In order to do so, download a free tool called reflector from http://reflector.red-gate.com.
In this deep dive, you will be decompiling important pieces of
SharePoint. Please note that it is OK to do so for learning purposes
only.
Once you have downloaded and installed reflector on
your SharePoint machine, drag and drop all the Microsoft.SharePoint.*
dlls from the 14\ISAPI folder into reflector. This will allow you to
examine the internal class structures of the SharePoint 2010 framework. I
should mention that the code you are about to look at is Microsoft code
and Microsoft can choose to change it at any point without consulting
us first.
With the Microsoft SharePoint classes loaded, look
for the Microsoft.SharePoint.ApplicationRunTime.SPRequestModule class.
This is an extremely important class in the Microsoft SharePoint
framework. This class is an HttpModule. In fact, it is the first
HttpModule that runs in the SharePoint pipeline. You can verify this by
opening the web.config of any SharePoint web site and looking for a
section, as shown in Listing 1.
Example 1. HttpModules in a SharePoint web.config
<modules runAllManagedModulesForAllRequests="true">
<remove name="AnonymousIdentification" />
<remove name="FileAuthorization" />
<remove name="Profile" />
<remove name="WebDAVModule" />
<remove name="Session" />
<add name="SPRequestModule" preCondition="integratedMode"
type="Microsoft.SharePoint.ApplicationRuntime.SPRequestModule, Microsoft.SharePoint,
Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" />
<add name="ScriptModule" preCondition="integratedMode"
type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, C
ulture=neutral, PublicKeyToken=31bf3856ad364e35" />
<add name="SharePoint14Module" preCondition="integratedMode" />
<add name="StateServiceModule" type="Microsoft.Office.Server.Administration.StateModule,
Microsoft.Office.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
/>
<add name="PublishingHttpModule" type="Microsoft.SharePoint.Publishing.PublishingHttpModule,
Microsoft.SharePoint.Publishing, Version=14.0.0.0, Culture=neutral,
PublicKeyToken=71e9bce111e9429c" />
</modules>
|
Put simply, possibly to the risk of inaccuracy, the SPRequestModule class is what turns an ASP.NET site into a SharePoint site.
Back in reflector, look for the Init method of the
SPRequestModule. Somewhere in the Init method, you will see a code block
as shown in the following:
SPVirtualPathProvider provider2 = new SPVirtualPathProvider();
HostingEnvironment.RegisterVirtualPathProvider(provider2);
_virtualPathProviderInitialized = true;
The SPVirtualPathProvider class shown previously is
what SharePoint uses to provide a virtual file system to SharePoint. The
SPVirtualPathProvider inherits from the VirtualPathProvider abstract
base class and provides implementations for the necessary methods such
as GetFile and GetDirectory. For the sake of space, let me get to the
point, but you can poke more around the framework using Reflector if you
so wish. The GetFile method creates an instance of SPVirtualFile, which
depending upon the request type would return either a
Microsoft.SharePoint.ApplicationRuntime.SPDatabaseFile or a
Microsoft.SharePoint.ApplicationRuntime.SPLocalFile. Most of these
classes are internal and sealed, but they are great tools for learning
how the product actually works.
As you can see, one of those files comes from the
database (site pages), and another comes from the file system
(application pages).
The behavior of application pages and site pages are
quite different from each other. Let's write a small Visual Studio
Solution that deploys an application page and a site page, and then
examine the differences between the two.
1. Deploying Pages: A Quick Example
Open Visual Studio 2010 and create a new empty
SharePoint project called SomePages. You will have to create this as a
farm solution because application pages live on the file system and
sandbox solutions cannot edit the file system.
Application pages live in a standard SharePoint
folder called the layouts folder. To add the layouts folder in your
project, right-click the project and choose add\SharePoint "layouts"
mapped folder. This action will add a layouts "SomePages" folder into
your solution structure. This is a good practice because the file system
is potentially shared across many solutions. Putting your application
pages in your own folder ensures that you don't accidentally overwrite
somebody else's pages.
Next, right-click the SomePages folder and choose to
add a new item. When prompted, go ahead and add the new application page
called MyApplicationPage.aspx. In a similar manner, add the new element
called Elements.xml and add a new module called SitePageModule. In the
SitePageModule, rename the sample.txt file to SitePage.aspx.
Finally, add a new feature called SomePages. Your project structure should look like the one shown in Figure 1.
The package is going to deploy an application page
and the SomePages feature. The SomePages feature when activated creates a
new custom action to access the application page under the site actions
menu, and it creates a new site page in the content database.
Open the MyApplicationPage.aspx file, and change the PlaceHolderMain ContentPlaceHolder content to the following:
<h1> Current Trust Level is:
<asp:Label ID="currentTrustLevel" runat="server" Text="Label"></asp:Label>
</h1>
Edit the code-behind of MyApplicationPage.aspx to what is shown in Listing 2.
Example 2. Code-Behind for MyApplicationPage.aspx
protected void Page_Load(object sender, EventArgs e)
{
currentTrustLevel.Text = GetCurrentTrustLevel().ToString();
}
private AspNetHostingPermissionLevel GetCurrentTrustLevel()
{
AspNetHostingPermissionLevel[] permissionLevels = new AspNetHostingPermissionLevel[]
{
AspNetHostingPermissionLevel.Unrestricted,
AspNetHostingPermissionLevel.High,
AspNetHostingPermissionLevel.Medium,
AspNetHostingPermissionLevel.Low,
AspNetHostingPermissionLevel.Minimal
};
foreach (AspNetHostingPermissionLevel trustLevel in permissionLevels)
{
try
{
new AspNetHostingPermission(trustLevel).Demand();
}
catch (System.Security.SecurityException)
{
continue;
}
return trustLevel;
}
return AspNetHostingPermissionLevel.None;
}
|
As you can see, the preceding code tries to get the
current AspNetHostingPermissionLevel and displays it on the page.
Deploying the application page makes it accessible for use to all
SharePoint web applications. After such a page is deployed, typing the
URL directly in the browser will open the page. However, you still need
to make it easier for the user to access it using a convenient link. One
possible way to add such a link is to add a custom action in
SharePoint. There are many places within SharePoint where a custom
action can be added. One such place is the site actions menu. In order
to add a custom action under the site actions menu, edit your
elements.xml to what is shown in Listing 3.
Example 3. Elements.Xml Used to Add the Custom Action
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/SharePoint/">
<CustomAction
Id="MyApplicationPage"
GroupId="SiteActions"
Location="Microsoft.SharePoint.StandardMenu"
Sequence="2001"
ImageUrl="_layouts/images/somepages/wslogo.gif"
Title="My ApplicationPage"
Description="This page will tell you what security level it is operating
under.">
<UrlAction Url="˜site/_layouts/SomePages/MyApplicationPage.aspx"/>
</CustomAction>
</Elements>
|
That finishes your application page so now you need to start working on the SitePage.
Under the SitePageModule folder, edit the SitePage.aspx file to include the following code:
<%@ Page Language="C#" MasterPageFile="~masterurl/default.master" %>
<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<h1>
Current Date and Time is: <% Response.Write(DateTime.Now); %>
</h1>
</asp:Content>
In the elements.xml in the SitePageModule folder, add the following code block:
<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/SharePoint/">
<Module Name="SitePageModule">
<File Path="SitePageModule\SitePage.aspx" Url="SitePageModule/SitePage.aspx">
<NavBarPage ID="1002" Name="SitePage" Position="1002"/>
</File>
</Module>
</Elements>
The module tag allows you to deploy artifacts into the content
database. In this case, you are deploying the SitePage.aspx file into
the SitePageModule folder in the content database. In addition, you are
also creating a menu on the navigation bar for easy access to the site
page.
Next, go ahead and build then deploy the solution. After deploying, visit http://sp2010
in your web browser and go to site actions \site settings. Click the
Manage site features link under the site actions section. You should see
the SomePages feature activated, as shown in Figure 2.
Also, since the feature is now active, pay close
attention to the navigation bar of the web site. You should see a link
to the SitePage menu as well. This can be seen in Figure 3.
In SharePoint Designer if you were to open your site
and click all files link on the left, you should see the SitePageModule
folder in the content database. This can be seen in Figure 4.
Inside that folder, you should see your SitePage.ASPX
file deployed. Back in your browser, go ahead and click the SitePage
link on the NAV bar an attempt to access the SitePage. You should see
the page run, as shown in Figure 5.
Next, make the following changes to the SharePoint web.config:
Change the SharePoint\SafeMode\@CallStack attribute to true.
Change System.Web\CustomErrors\@Mode attribute to On.
Change System.Web\Compilation\Debug mode to true.
The above changes will allow you to view any errors in their full detail through the browser.
With the above changes made, edit your site page in
either visual studio or SharePoint designer, and embed some server side
code using the <script runat="server"> tag, and you will notice
that SharePoint informs you that server side script/code blocks are
prohibited from running in SitePages by default.
You get this error because by default code blocks are
not allowed in site pages. To allow code blocks in site pages, you have
to allow for exceptions, using PageParserPaths element in the
web.config of SharePoint.
Specifically, you would have to add the following
code block under the SharePoint element in the web.config of your
SharePoint web site.
<PageParserPaths>
<PageParserPath VirtualPath="/SitePageModule/*" CompilationMode="Always"
AllowServerSideScript="true" IncludeSubFolders="true"/>
</PageParserPaths>
However, using PageParserPaths is not a good
practice. This is because now anyone with access to the content database
via SharePoint Designer or otherwise will have the ability to upload
and run any arbitrary code on the server. The second disadvantage of
this approach is the necessity to edit the web.config to make your code
run.
Next, let's look at the application page in action.
With your feature now activated, you should see a link to your
application page under the site actions menu, as shown in Figure 6.
Accessing the application page would successfully run it in SharePoint. Note the URL of the application page is the following: http://sp2010/_layouts/SomePages/MyApplicationPage.aspx.
Next, try visiting the same application page in central administration by changing the URL to: http://sp2010:40000/_layouts/SomePages/MyApplicationPage.aspx.
NOTE
40000 is the port for Central Administration on my machine.
Also, create a subsite called "SubSite " under your
port 80 root level site collection, and visit the same application page
at the URL: http://sp2010/subsite/_layouts/SomePages/MyApplicationPage.aspx.
Notice that the same application page is available at
every single location within SharePoint. I didn't even have to activate
the feature, because the physical file for the above URL is being
shared under each of these locations. In fact, it is being served by the
same physical file, so by editing that one file you change it
everywhere within SharePoint. This behavior is different from site pages
because editing a site page only changes it at the specified location.
Also, running the page should present you the current trust level, as
shown in Figure 7.
Also, note that the application page runs under the
ASP.NET trust level of unrestricted. Therefore, if you're deploying
custom application pages you should always think of securing them as
well. In order to secure an application page, you have two choices.
Either you can require the user to be a site collection administrator or
you can specify an explicit permissions string necessary to access the
application page.
In order to secure your application page and restrict
its usage to only the site collection administrator, you need to make
the following code changes.
To secure the page itself, add the following code in the code behind of MyApplicationPage.aspx:
protected override bool RequireSiteAdministrator
{
get
{
return true;
}
}
In the elements.xml used to create the CustomAction, add the following attribute under the CustomAction element:
RequireSiteAdministrator="true"
Now redeploying and reactivating your feature will
give you an access denied if you try accessing the application page as a
user who is not a site collection administrator. If, instead, you
wanted to restrict using a permission string, you would have to
overwrite the RightsRequired property in the code behind, and specify
the rights attribute in the custom action element.
One other thing I'd like you to try before leaving
this exercise is to deactivate the feature and retract a solution from
Central Administration. Note that deactivating the feature successfully
removes the custom action under site actions. Also, retracting the
solution removes the application page from the file system.
Interestingly, the site page in the content database and the relevant
navigation bar link are not removed when the feature is deactivated or
the solution is retracted. Therefore, to clean up the site page
artifacts, you would have to write a custom feature receiver and
override the feature deactivating event. Remember when I had mentioned, as a very rough rule of thumb, stuff
that goes in the content database is left up to you to clean and stuff
that goes on the file system is generally cleaned up for you?
The following list summarizes the major differences between an application page and a site page:
An application page is deployed to the file system and a SitePage is deployed to the content database.
An application page requires a farm solution and a SitePage can be deployed using a sandbox solution.
An application page is shared all across the web front end, but a SitePage is unique to its location.
A SitePage can be easily customized through SharePoint Designer, but an application page usually requires Visual Studio.
Custom application pages should be secured and PageParserPaths for site pages should be avoided.
Feature
Receivers are necessary to clean up after a site page feature is
deactivated. Application page artifacts are removed from the file system
when the solution is retracted.
The obvious question here is what is an application
page or site page good for? In which situation would you pick which?
Application pages generally are great for administrative-like functions.
For example, all the layouts pages (those that appear at _layouts URLs)
are application pages that Microsoft wrote. You should never edit out
of the box Microsoft pages or any other out of the box files for that
matter. But if you wish to deploy custom application pages, your own
folder in the layouts directory is the right place to deploy your custom
application pages.
On the other hand, site pages are great for
presenting content and functionality to the end user. They also easily
adapt the look and feel using the default.master of your SharePoint web
site. WebPart pages are special example of SitePages. These are simply
SitePages with WebPartZones in them. The WebPart manager itself lives on
the master page. Therefore, putting WebPartZones on a site page, allows
you to create a page in which during feature activation, or at a later
date you can put WebParts.
In the next section, I will cover writing
the details of a WebPart and also deploy that WebPart using a site page
that has WebPartZones.