Monday, January 12, 2009

Exploring the 12 Hive : TEMPLATE Directory

Source: http://www.pointsharepoint.com/2008/08/exploring-12-hive-template.html

We will take a look at the structure of the file system in SharePoint 2007 and how the files are used. As a developer you must know about the 12 HIVE folder.

If we look at a standard Microsoft Office SharePoint 2007 installation we can see that a new directory has been created under the following;
C:\Program Files\Microsoft Shared\Common Files\web server extensions
You will now see a 12 directory. This is known as the "12 HIVE" and is the directory that holds the necessary system files that drive SharePoint 2007.

The following directories can be found under the 12 hive;
ADMISAPI
BIN
CONFIG
HCCab
Help
ISAPI
LOGS
Resources
TEMPLATE
For the purposes of SharePoint development you will, for the majority of the time, be working with the TEMPLATE directory.

The TEMPLATE DirectoryHere is the structure under the TEMPLATE directory;

1033 - ADMIN - CONTROLTEMPLATES - DocumentTemplates - FEATURES - GLOBAL - IMAGES - LAYOUTS - Pages - SiteTemplates - SQL - THEMES - XML

The TEMPLATE\1033
Directory : There are two directories that you need to know about here. The Workflow directory is where you will find the WSS.ACTIONS file. This file is used by SharePoint Designer 2007's Workflow Designer. All of the workflows functions available to Designer are defined here. If you create workflow functionality using Visual Studio 2005/2008 and you want the functions to appear within the Workflow Designer, you will need to create a new .ACTIONS file.

The TEMPLATE\1033\STS Directory : The SharePoint Team Services or STS directory has one sub-directory within it. The DOCLIB directory contains several further sub-directories, each of which holds a template for a specific content type. There are templates for the Office applications, Word, Excel, PowerPoint and OneNote. There are also templates for the different types of page that can be created in SharePoint.

The TEMPLATE\1033\XML Directory : A more important directory within 1033 is the XML directory. Within this directory are several XML files.

Note : Changes to any of the file could be overwritten by SharePoint Service Packs.

DEADWEB.XML
– This file is used during the site expiration process. Only change this file if you need to change the message that is displayed during the site expiration process.
RGNLSTNG.XML – This file holds the regional settings used by SharePoint 2007. You should not need to change this XML file.
WEBTEMP.XML – This file defines the pointers to the GLOBAL, STS, MPS, CENTRALADMIN, WIKI and BLOG site definitions. You can amend this file to either display or hide site templates during the New Site Creation process (newsbweb.aspx and scsignup.aspx).

WEBTEMPBDR.en-US.XML – This file defines the pointers to the BDR site definition. You can amend this file to either display or hide site templates during the New Site Creation process (newsbweb.aspx and scsignup.aspx).

WEBTEMPOFFILE.XML - This file defines the pointers to the OFFILE site definition. You can amend this file to either display or hide site templates during the New Site Creation process (newsbweb.aspx and scsignup.aspx).
WEBTEMPOSRV.XML - This file defines the pointers to the OSRV site definition. You can amend this file to either display or hide site templates during the New Site Creation process (newsbweb.aspx and scsignup.aspx).
WEBTEMPSPS.XML - This file defines the pointers to the SPS, SPSPERS, SPSMSITE, SPSTOC, SPSTOPICM, SPSNEWS, CMSPUBLISHING, BLANKINTERNET, SPSNHOME, SPSSITES, SPSCOMMU, SPSPORTAL, SRCHCEN, PROFILES, BLANKINTERNETCONTAINER and SPSMSITEHOST site definitions. You can amend this file to either display or hide site templates during the New Site Creation process (newsbweb.aspx and scsignup.aspx).
WEBTEMPSRCH.XML - This file defines the pointers to the SRCHCENTERLITE site definition. You can amend this file to either display or hide site templates during the New Site Creation process (newsbweb.aspx and scsignup.aspx).
It is preferable to clone and existing site definition and associated WEBTEMPxxx.XML file rather than edit an existing one. This will prevent your changes from being overwritten if a Service Pack is applied.
The final directory under 1033 is STS. This directory holds a subdirectory, DOCTEMP which contains further subdirectories that hold the definitions and templates for document types that are associated with document libraries. You will find templates for Microsoft Word, Excel and PowerPoint amongst others. You should not need to amend any of these files.

The TEMPLATE\ADMIN Directory : The files in the ADMIN directory are used by the Central Administration pages. There are three directories within ADMIN; 1033, Content Deployment and SSO. 1033\Policy\Report contains a reporting template and sample data. The Content Deployment directory contains the DeploymentUpload.aspx page. Finally, SSO contains all of the aspx pages for Single Service Sign-On. These files should not be amended.

The TEMPLATE\CONTROLTEMPLATES Directory : The CONTROLTEMPLATES directory is where the ASP.NET 2.0 control template files are held. Control templates are small re-usable files that contain components of a web page. An example is welcome.ascx. The welcome.ascx file defines the dropdown menu that appears under Welcome name on the top navigation section of a SharePoint 2007 page.
These pages are pulled into SharePoint 2007 aspx pages. You can create your own ascx pages.

The TEMPLATE\DocumentTemplates Directory : This directory contains one file, wkpstd.aspx, which is the base master file for Wiki pages.
Tips & Tricks : It is worth noting that although this page can be cloned and modified it will not be used by any Wiki page other than the first page that is created. This is because the wkpstd.aspx page is hard coded and is always used to create further Wiki pages.

The TEMPLATE\FEATURES Directory : The FEATURES directory holds subdirectories for each Feature that is available within SharePoint 2007. Each of these subdirectories will hold at least one file, feature.xml – this file will define the Feature and will have pointers to any other files required by the Feature.

The TEMPLATE\GLOBAL Directory : The GLOBAL directory holds two very important files; default.master and mwsdefault.master.
The default.master file is the master file that all files are based on. If you change this file, the change will be applied to every page that was created using an out-of-the-box site definition.

The mwsdefault.master file is the master file that all Meeting Workspace files are based on. If you change this file, the change will be applied to every Meeting Workspace page that was created using an out-of-the-box site definition.

The GLOBAL\XML directory holds the master ONET.XML file and master view XML files.
You should not need to modify any of the files within the Lists or XML directories at this time.

The TEMPLATE\IMAGES Directory : The IMAGES directory holds all images used within SharePoint 2007.

The TEMPLATE\LAYOUTS Directory : The LAYOUTS directory is another one of those directories that you should pay attention to. If you ever see /_layouts/ in the URL of one of your SharePoint sites, the aspx page will be in this directory. You will also notice the odd .ascx (control template), .js (JavaScript) and .master file within this directory. I am not sure why Microsoft didn't tidy these files up and put them in more appropriate directories – as you become more familiar with SharePoint you will notice little inconsistencies like this.
In the LAYOUTS directory is application.master, the master file for all of the "admin" type pages, such as upload.aspx. When you begin to look at site customisation and branding you will see that application.master is one of the files that will need to be looked at.
The 1033\IMAGES directory : contains many of the images used in publishing sites, such as the thumbnails for each of the different pages types. There are also icons in this directory.
The 1033\STYLES directory : contains additional cascading style sheet (CSS) files.
The MOBILE directory : contains a series of aspx pages that are optimised for mobile devices.
The STYLES directory : contains one file, corefixup.css – more of an afterthought by Microsoft?

The TEMPLATE\Pages Directory : The Pages directory contains three aspx pages that are pulled in as subpages to other aspx pages.

The TEMPLATE\SiteTemplates Directory : A slightly misleading directory name as the SiteTemplates directory actually contains all of the SharePoint Site Definitions. It is in this directory that you would create any new site templates. There is a sub-directory for each site definition. Each sub-directory will contain at least an XML directory and a default.aspx file. The aspx file is the initial home page that is displayed when a site definition is used in SharePoint. The XML directory : will contain at least an ONET.XML file, which defines the setup of the site definition, such as which Features to load, where the web parts go and what they are, which document library templates to assign.
There will be extra files in some of the directories but they are used to provide the extra functionality that the site definitions require.
When creating new site definitions, you will place your directories here. Please have a look at our blog for creating custom site definitions in more detail.

The TEMPLATE\SQL Directory : The SQL directory contains a series of XML and SQL files. Some of these are used to define SharePoint configuration and content databases. You should not change these files.

The TEMPLATE\THEMES Directory : The THEMES directory contains 22 sub-directories, one for each theme that is available within SharePoint. Each sub-directory contains all of the files required for the theme, including all the images and the cascading style sheets.
You can create your own custom theme and place it within the THEMES directory. Please visit our blog Creating Custom Themes.

The TEMPLATE\XML Directory : The final directory, XML, contains XML and XSD files that are used in configuration of SharePoint. You shouldn't need to venture in this directory, but if you do, be aware that any file you modify may be overwritten by SharePoint Service Packs.

alerttemplates.xml : Used to change the look and feel of Alert Notification emails for each list, web or custom types in your SharePoint Environment. You can set custom branding based on your to your email alerts.

BASE.XML : defines base types for lists that apply globally to all the SharePoint Web sites.

DOCICON.XML : This file is used for mapping file types to particular icons. To add the new icon to the file type follow below steps.
1). To add a .pdf (Adobe PDF) file name extension, add the following line to the ByExtension section in this file—

'<' Mapping Key="pdf" Value="pdf16.gif"/ '>'.
2). Add the file pdf16.gif (use Google Image Search to get this image) to the Program Files\Common Files\Microsoft Shared\web server extensions\12\Template\Images.

FLDTYPES.XML : Used to define how various SharePoint field types are rendered.

fldtypes_hold.xml : Used to define the hold status pertaining to entities realted to Records Management Policy. Note: This file is meant for internal use and it is recommended not alter the contents.

fldtypes_publishing.xml : Used to define the publishing field types like HTML, Links, Summary Links etc. In case of creating a new custom publishing field type, say Address (with validation) then a new entry related to this filed type has to be added to this file.All other xml files with prefix fldtypes are cutom built field types. Yet another notable field type is Business Data field. This field type is defined in fldtypes_spsbizdata.xml.

htmltransinfo.xml : Used to define the how to open various documents stored in the sharepoint server. The mapping entry for a particular document in this XML file will tell SharePoint server to open the document in a particular client application. If the document conversion mapping is not found in this file, SharePoint will prompt the user to download the document.

SharePoint is flexible enough for you to create custom properties by creating custom XML files in this directory.

This is a good start for you to go off exploring further in TEMPLATES Directory.

Thursday, January 8, 2009

InfoPath Forms in Office SharePoint Server 2007

Source Andrew May

InfoPath Forms in Office SharePoint Server 2007

I'm sure it comes as no surprise that Office InfoPath 2007 is the forms designer of choice for Office SharePoint Server 2007. But the average SharePoint developer, used to reaching for ASP.NET when he needs to create a form for SharePoint, might be surprised at all the places you can employ InfoPath to quickly create forms for enterprise management functions.

The main reason for this is the fact that in InfoPath 2007 you can create what the InfoPath guys are calling symmetrical forms. Symmetrical forms are InfoPath forms that can be hosted either in the Office client applications like Word, PowerPoint, and Excel, as well as in the SharePoint Server browser interface. So you can create a single form, and know it'll look and work the same whether the user sees it in an Office application or in the browser interface.

(How is this possible? Short version: SharePoint Server 2007 uses Office Forms Services, a server-based run-time environment for InfoPath 2007 forms, to host the forms in the browser. Office Forms Services consumes the forms you create in the InfoPath client and renders them in an ASP.NET framework, which acts as a run-time environment for the form. This environment presents a form editing experience that matches the InfoPath 2007 client application. The Office client applications, on the other hand, include the ability to host the native InfoPath forms. )

So naturally, symmetrical forms are very handy for enterprise content management areas, where the user might be working with documents in their native client application, or online through the SharePoint Server browser interface.

Here're a couple of the places where you can employ InfoPath forms for enterprise content management in SharePoint Server 2007:

         Document Information Panels

A document information panel is a form that is displayed within the client application, and which contains fields for the document metadata. Document information panels enable users to enter important metadata about a file anytime they want, without having to leave the Microsoft Office system client application. For files stored in document libraries, the document information is actually the columns of the content type assigned to that file. The document information panel displays a field for each content type property, or column, the user can edit.

You can create document information panels either from within SharePoint Server, or directly from InfoPath 2007.

How to: Create or Edit a Custom Document Information Panel from within Office SharePoint Server 2007

How to: Create a Custom Document Information Panel from InfoPath

         Workflow Forms

You can create InfoPath forms for use with workflows in SharePoint Server 2007. That way, the user can interact with the workflow form from within the Office client application, and not just through the browser.

SharePoint Server 2007 uses Office Forms Services to display workflow forms, be they association, initiation, modification, or edit task forms. The only difference is that there's a different .aspx page hosting the Office Forms Services control for each type of workflow form. Initiation forms are hosted by a different .aspx page than modification forms, for example. Each different hosting page knows how to submit the information from its type of form to SharePoint Server (and hence, to the workflow engine).

The .aspx pages that contain the Office Forms Services web part are included as part of SharePoint Server, of course.

How to: Design an InfoPath Form for a Workflow in Office SharePoint Server 2007

So if your putting together an enterprise content management solution in Office SharePoint Server, and you'd like your user to be able to interact with your custom forms in the client application and the browser, it might be worth your while to take a look at InfoPath 2007 and Office Forms Services.

Using the SPPropertyBag with Custom Admin Pages in SharePoint

Source Steve Graegert
Using the SPPropertyBag with Custom Admin Pages in SharePoint


 

A couple of months ago, I started a MOSS 2007 project which was intended as a collaborative environment. In some cases, we wanted to read data from other data sources, one of them being a DB2 UDB, and display it in SharePoint. In order to successfully connect to a database you need a connection string, and you need to store it securely since it usually contains the required credentials to access the DB.

In this post I will explain how we can use SharePoint's SPPropertyBag to securely store strings of information and how we can use admin pages to interact with the data from within the SharePoint 2007 web interface.

This is what we are trying to achieve:


What we see here is an application page in the site/web adminitration area allowing admins to store web/application-related information.

You can download the source code for the application page, the required Feature to enable this functionality and the complete code for the SPProperties type from here: Source Code: SPProperties Article

Note: This is a Visual Studio 2008 Solution, get Microsoft Visual Studio 2008 Express from here.

Prerequisites

So, where do you store your (connection) strings then? I usually like the web.config approach with a connectionString key, but that's not very secure, it's as secure as the server hosting the web.config file. Why don't we put it in the same DB SharePoint writes its content to? Great idea, but how do I access the DB which I do not know the structure of? BTW, messing around SharePoint content DBs is a big no-no! This is where SPPropertyBag comes in very handy.

The SPPropertyBag Type

Every SharePoint site (represented by the SPWeb type in the object model) is associated a Properties property of type SPPropertyBag with the site's settings such as the version of Windows SharePoint Services and the language locale.

We can use a site's SPPropertyBag to store any site-related information as we'll see shortly. To obtain the site's Properties simply use the following syntax:

SPWeb spWeb =
(new SPSite("http://dev.local")).OpenWeb();

SPPropertyBag spProperties = spWeb.Properties;

 
 

foreach
(DictionaryEntry entry in spProperties)

Console.WriteLine(" {0,-25} {1}", entry.Key, entry.Value);

As you can see from the code an SPProperyBag implements the StringDictionary interface which implements a hash table with the key and the value strongly typed to be strings rather than objects. That simply means, we can add new items by specifying the key and value as strings, illustrated in the following code snippet:

private
void SetValue(String key, String value, String siteUrl)
{

SPWeb spWeb =
(new SPSite("http://dev.local")).OpenWeb();

SPPropertyBag spProperties = spWeb.Properties;

 
 


if
(spProperties.ContainsKey(key))
{

spProperties[key]
= Value;


}
else
{

spProperties.Add(Key, Value);


}

 
 

spProperties.Update();

}

This method adds or sets an item in the Properties property of the site specified by siteUrl. The call to Update causes SharePoint to persist the changes to the database. Normally, you can obtain a reference to sites (the site collection's root web site, for instance) using a call similar to these:

// gets the site collection's root web site

SPWeb spWeb = SPContext.Current.Site.RootWeb

// gets the current web site from the request's context

Web = SPControl.GetContextWeb(Context);

.

The Helper Assembly

Storing settings is a requirement almost every application must fulfill and everytime again, developers and software architects must decide where to store application settings. We can choose from many options: databases, XML files, resource files and binary data, to name a few.

Getting and setting application data is a common operation, independent of the technology used to persist the data. It is therefore desirable to create a reusable and extensible approach to the problem: storage and retrieval of application data without taking into account the persisting technology.

Let's create an abstract class Settings which will be the base class for all the different solutions to the problem statement. TheSettings class defines the two self-descriptive methods ExistsKey and GetValue. The indexer allows for simple access to the collection using string values as keys:

using
System;

using
System.Reflection;

 
 

namespace SG.Global
{


public abstract class Settings {


protected Settings()
{
}

 
 


public abstract bool ExistsKey(String key);


protected abstract String GetValue(String key);

 
 


public
String
this[String key]
{

get {
return
this.GetValue(key); }


}


}

}

Based on this class we can derive many types, depending on the different data stores. We could, for example, inherit from Settings to allow reading and writing config file settings:

public
class ConfigFileSettings : SG.Global.Settings
{


public
override
bool Existskey(String key)
{


return
(ConfigurationSettings.AppSettings[key]
!=
null);


}

 
 


protected
override
String GetValue(String key)
{


return ConfigurationSettings.AppSettings[key];


}

}

Next, we need to derive from Settings in order to implement the SharePoint version for reading and writing SPWeb properties:

using
System;

using
System.Collections.Generic;

using
System.Text;

 
 

using
Microsoft.SharePoint;

using
Microsoft.SharePoint.Utilities;

 
 

namespace SG.Global.SharePoint
{


public
class SPProperties : SG.Global.Settings
{


private SPPropertyBag spProperties;


private SPWeb webCurrent;

 
 


public SPProperties(SPWeb webCurrent)
:
base()
{


this.webCurrent
= webCurrent;

spProperties =
this.webCurrent.Properties;


}

 
 


public
override
bool ExistsKey(String key)
{


return spProperties.ContainsKey(key);


}

 
 


protected
override
String GetValue(String key)
{


if
(ExistsKey(key))
{


return
(String)spProperties[key];


}
else
{


return
String.Empty;


}


}

 
 


public
void SetValue(String key, String value)
{


if
(ExistsKey(key))
{

spProperties[key]
= value;


}
else
{

spProperties.Add(key, value);


}

 
 

spProperties.Update();


}

 
 


public
void DeleteKey(String key)
{

spProperties.Remove(key);

spProperties.Update();


}

 
 


public SPWeb CurrentWeb {

get {
return webCurrent; }

set { webCurrent = value; }


}


}

}

The constructor takes an SPWeb argument and the methods GetValue, SetValue, DeleteKey, ExistsKey to what you think they do: getting, setting, deleting items and checking for their existence.

So far, so good, but what does all this have to do with strings of information and admin pages? Glad you asked!

The Admin Pages

Whenever you click the Site Settings menu item in the Site Actions menu you're entering the backend area. The following screenshot shows the admin area of a typical Web within a site collection.


Please note the last link My Custom Settings in the column Site Collection Administration. Klicking it will open a custom admin page similar to the one you've seen at the beginning of this article.

The Page Structure

Creating custom admin pages is not very difficult and I recommend starting with an existing page from the Layouts directory. All application pages have a similar structure and allow adding and removing controls as you wish. To avoid having to deal with the layout of these admin pages SharePoint encapsulates all the supported controls in assemblies and user controls you can reference and use in your code the same way you do in traditional ASP.NET development.

The following graphics illustrates the basic structure of such an administration page:


Let's take a look at the source code and compare the graphics to the code (some elements removed for clarity):

<%@ [directives removed for brevity] %>

 
 

<script runat="server">

/* your code */

</script>

 
 

<asp:Content
ID="Content2" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">


<SharePoint:EncodedLiteral runat="server"
Text="My Portal Settings" EncodeMethod='HtmlEncode'
/>

</asp:Content>

 
 

<asp:Content
ID="Content3" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">


<SharePoint:EncodedLiteral runat="server"
Text="My Portal Settings" EncodeMethod='HtmlEncode'
/>

</asp:Content>

 
 

<asp:Content
ID="contentMain" ContentPlaceHolderID="PlaceHolderMain" runat="server">


<table
class="ms-settingsframe">


<tr>


<td
width="100%"
colspan="4"
style="padding-top: 0px;">


<table
class="ms-pageinformation">


<tr>


<SharePoint:EncodedLiteral ID="EncodedLiteral1"
/>


<SharePoint:EncodedLiteral ID="EncodedLiteral2"
/>


</tr>


</table>


</td>


</tr>


<tr>


<td>


<table
class="ms-propertysheet">


<wssuc:InputFormSection Title="Global Settings"
id="ifmSQL" Description="...">


<template_inputformcontrols>


<wssuc:InputFormControl LabelText="Domain (NETBIOS)" runat="server">


<Template_Control />


</wssuc:InputFormControl>


<wssuc:InputFormControl

LabelText="Error Logging" runat="server" Description="...">


<Template_Control />


</wssuc:InputFormControl>


</template_inputformcontrols>


</wssuc:InputFormSection>


<wssuc:InputFormSection Title="Exchange Server Settings"


id="ifmExchange" Description="..." runat="server">


<template_inputformcontrols>


<wssuc:InputFormControl

LabelText="Exchange Server OWA Address" runat="server">


<Template_Control />


</wssuc:InputFormControl>


</template_inputformcontrols>


</wssuc:InputFormSection>


<SPSWC:InputFormButtonSection runat="server">


<SPSWC:InputFormButtonAtBottom

runat="server"
ID="cmdOK"
OnClick="OnClickOK"
/>


<SPSWC:InputFormButtonAtBottom

runat="server"
ID="cmdCancel"
OnClick="OnClickCancel"
/>


</SPSWC:InputFormButtonSection>


</table>


</td>


</tr>


</table>

</asp:Content>

As you can see, we do not need to specify additional styles or other elements to create pages following SharePoint's default design. Everything is neatly hidden in user and server controls.

Page Layout by Dissection

Depending on your needs we'll have to register several tag prefixes and import some namespaces:

wssuc

The wssuc tag prefix is used for all user controls being imported from the filesystem (12\TEMPLATE\ControlTemplates). In this example, the three controls InputFormSection.ascx, InputFormControl.ascx and ButtonSection.ascx are responsible for rendering the page layout and used in all admin pages, so you'll most likely need them quite often.

wssawc

The wssawc tag prefix is not shown in the code example above but you'll need it for all interactive controls like textboxes and buttons. In our custom admin page all textboxes, checkboxes and buttons are referenced through this prefix. We will see a fully functioning example shortly.

SPSWC

The SPSWC tag prefix identifies controls provided by MOSS via namespace Microsoft.SharePoint.Portal.WebControls. It is used for page layout and interactive components as well.

SharePoint

The SharePoint tag prefix is from Microsoft.SharePoint.WebControls and the most common one. You'll find lots of controls from this namespace all over WSS 3.0 and MOSS 2007, in master pages, custom controls, etc.

The <script> block is the right place for our code. Following the script block we can identify two very common content place holders, namely PlaceHolderPageTitle and PlaceHolderPageTitleInTitleArea, the former sets the browser window's title and the latter sets the title for the admin page as we can see in the figure above.

Various controls are responsible for the correct layout and assembly of appropriate HTML code. The first wssuc:InputFormSection renders the following HTML:

<tr
id="ctl00_PlaceHolderMain_ifmSQL">


<td
valign="top"
class="ms-descriptiontext">


<table
width="100%"
cellspacing="0"
cellpadding="1"
border="0">


<tbody>


<tr>


<td
valign="top"
height="22"
style="padding-top: 4px;"
class="ms-sectionheader">


<h3
class="ms-standardheader">Global Settings</h3>


</td>


</tr>


<tr>


<td
class="ms-descriptiontext ms-inputformdescription">

Logging and permissions for i5 users


</td>


<td><img
width="8"
height="1"
alt=""
src="/_layouts/images/blank.gif"/></td>


</tr>


<tr>


<td><img
width="150"
height="19"
alt=""
src="/_layouts/images/blank.gif"/></td>


</tr>


</tbody>


</table>


</td>


<td
valign="top"
align="left"
class="ms-authoringcontrols ms-inputformcontrols">


<table
width="100%"
cellspacing="0"
cellpadding="0"
border="0">


<tbody>


<tr>


<td
width="9"><img
width="9"
height="7"
alt=""
src="/_layouts/images/blank.gif"/></td>


<td><img
width="150"
height="7"
alt=""
src="/_layouts/images/blank.gif"/></td>


<td
width="10"><img
width="10"
height="1"
alt=""
src="/_layouts/images/blank.gif"/></td>


</tr>


<tr>


<td></td>


<td
class="ms-authoringcontrols">


<table
width="100%"
cellspacing="0"
cellpadding="0"
border="0"
class="ms-authoringcontrols">


<tbody>


<tr
id="ctl00_PlaceHolderMain_ifmSQL_ctl00_tablerow1">


<td
nowrap="nowrap"
colspan="2"
class="ms-authoringcontrols">


<span
id="ctl00_PlaceHolderMain_ifmSQL_ctl00_LiteralLabelText">

Domain (NETBIOS)


</span>


</td>


</tr>


<tr
id="ctl00_PlaceHolderMain_ifmSQL_ctl00_tablerow2">


<td><img
width="1"
height="3"
alt=""
src="/_layouts/images/blank.gif"/></td>


</tr>


<!-- End Right_Text -->


<tr
id="ctl00_PlaceHolderMain_ifmSQL_ctl00_tablerow3">


<td
width="11">


<img
width="11"
height="1"
alt=""
src="/_layouts/images/blank.gif"/>


</td>


<td
width="99%"
class="ms-authoringcontrols">


<input
type="text" alwaysenablesilent="true"
class="ms-input"


id="ctl00_PlaceHolderMain_ifmSQL_ctl00_tbDomain"
size="60"


name="ctl00$PlaceHolderMain$ifmSQL$ctl00$tbDomain"/>


<span
style="color: Red; display: none;"


id="ctl00_PlaceHolderMain_ifmSQL_ctl00_reqDomain">


<br/>Required


</span>


</td>


</tr>


<tr
id="ctl00_PlaceHolderMain_ifmSQL_ctl00_tablerow5">


<td><img
width="1"
height="6"
alt=""
src="/_layouts/images/blank.gif"/></td>


</tr>


<tr
id="ctl00_PlaceHolderMain_ifmSQL_ctl01_tablerow1">


<td
nowrap="nowrap"
colspan="2"
class="ms-authoringcontrols">


<span
id="ctl00_PlaceHolderMain_ifmSQL_ctl01_LiteralLabelText">Error Logging</span>


</td>


</tr>


<tr
id="ctl00_PlaceHolderMain_ifmSQL_ctl01_tablerow2">


<td><img
width="1"
height="3"
alt=""
src="/_layouts/images/blank.gif"/></td>


</tr>


<!-- End Right_Text -->


<tr
id="ctl00_PlaceHolderMain_ifmSQL_ctl01_tablerow3">


<td
width="11">


<img
width="11"
height="1"
alt=""
src="/_layouts/images/blank.gif"/>


</td>


<td
width="99%"
class="ms-authoringcontrols"></td>


</tr>


<tr><td
class="ms-formspacer"
/></tr>


<tr>


<td
class="ms-formspacer">


<img
width="10"
src="/_layouts/images/trans.gif"
alt=""/>


</td>


<td
width="100%"
class="ms-authoringcontrols">


<table
cellspacing="0"
cellpadding="0"
class="ms-authoringcontrols">


<tbody>


<tr>


<td
valign="top"
nowrap="nowrap">


<span
class="ms-input">


<input
type="checkbox"
name="ctl00$PlaceHolderMain$ifmSQL$ctl01$cbxLogging"


id="ctl00_PlaceHolderMain_ifmSQL_ctl01_cbxLogging"/>


</span>


</td>


<td>


<label
for="ctl00_PlaceHolderMain_ifmSQL_ctl01_cbxLogging">

Globally Enable Error Logging?


</label>


</td>


</tr>


</tbody>


</table>


</td>


</tr>


<tr
id="ctl00_PlaceHolderMain_ifmSQL_ctl01_tablerow5">


<td><img
width="1"
height="6"
alt=""
src="/_layouts/images/blank.gif"/></td>


</tr>


</tbody>


</table>


</td>


<td
width="10"><img
width="10"
height="1"
alt=""
src="/_layouts/images/blank.gif"/></td>


</tr>


<tr>


<td></td>


<td><img
width="150"
height="13"
alt=""
src="/_layouts/images/blank.gif"/></td>


<td></td>


</tr>


</tbody>


</table>


</td>

</tr>

Wow, that's a pile of HTML code, isn't it. With minimal effort we can leverage the power of SharePoint without having to worry about styles and layout.

To create a completely funtional page for our own purpose we need the following:

wssuc:InputFormSection

An InputFormSection provides the basic layout for each section, separated by a thin gray line. We will create two sections: one for the Global Settings and another one for the Exchange Server Settings

wssuc:InputFormControl

The wssuc:InputFormControl is the container for the actual SharePoint-specific controls. It renders a label and allows multiple controls in a single container. In our case we create a new wssuc:InputFormControl container for each InputFormControl.

wssawc:InputFormTextBox

A textbox similar to the traditional ASP.NET component. It's not augmented with additional functionality.

wssawc:InputFormRequiredFieldValidator

SharePoint's specially crafted RequiredFieldValidator. In our example every input control is being validated by a properly configured wssawc:InputFormRequiredFieldValidator.

SPSWC:InputFormCheckBox

A checkbox for use within wssuc:InputFormControl containers.

SPSWC:InputFormButtonSection

Renders a button container for the page's OK and Cancel buttons.

SPSWC:InputFormButtonAtBottom

Renders a simple button to do the page post-back or other actions, depending on the OnClick property setting.

Building the Page

Now that we have identified what is needed to create our admin page, we can try assemble it. C# code is added later.

<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master" %>

 
 

<%@ Register TagPrefix="wssuc" TagName="InputFormSection"


Src="~/_controltemplates/InputFormSection.ascx" %>

<%@ Register TagPrefix="wssuc" TagName="InputFormControl"


Src="~/_controltemplates/InputFormControl.ascx" %>

<%@ Register TagPrefix="wssuc" TagName="ButtonSection"


Src="~/_controltemplates/ButtonSection.ascx" %>

<%@ Register TagPrefix="wssawc" Namespace="Microsoft.SharePoint.WebControls"

Assembly="Microsoft.SharePoint, Version=12.0.0.0,

Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register TagPrefix="SPSWC" Namespace="Microsoft.SharePoint.Portal.WebControls"

Assembly="Microsoft.SharePoint.Portal, Version=12.0.0.0,

Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"

Assembly="Microsoft.SharePoint, Version=12.0.0.0,

Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities"

Assembly="Microsoft.SharePoint, Version=12.0.0.0,

Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Register TagPrefix="SG" Namespace="SG.Global.SharePoint"

Assembly="Test.SPProperties, Version=1.0.0.0,

Culture=neutral, PublicKeyToken=fb3603850412338a" %>

 
 

<%@ Import Namespace="Microsoft.SharePoint" %>

<%@ Import Namespace="System.Xml" %>

<%@ Import Namespace="SG.Global.SharePoint" %>

 
 

<script runat="server">

/* put code here */

</script>

 
 

<asp:Content
ID="Content2" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">


<SharePoint:EncodedLiteral runat="server"
Text="My Portal Settings" EncodeMethod='HtmlEncode'
/>

</asp:Content>

 
 

<asp:Content
ID="Content3" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">


<SharePoint:EncodedLiteral runat="server"
Text="My Portal Settings" EncodeMethod='HtmlEncode'
/>

</asp:Content>

 
 

<asp:Content ContentPlaceHolderID="PlaceHolderPageImage" runat="server">

</asp:Content>

 
 

<asp:Content
ID="contentMain" ContentPlaceHolderID="PlaceHolderMain" runat="server">


<style
type="text/css">

table.ms-propertysheet {

height: 100%;

}


</style>


<table
cellspacing="0"
cellpadding="0"
border="0"
class="ms-settingsframe">


<tr>


<td
width="100%"
colspan="4"
style="padding-top: 0px;">


<table
class="ms-pageinformation"
width="100%"
cellpadding="0"
cellspacing="0">


<tr>


<td
valign="top"
style="padding: 10px;"
width="100%"
height="100px">


<table
height="100%"
width="100%"
id="idItemHoverTable">


<tr>


<th
scope="col"
colspan="2"
style="padding-bottom: 8px;">


<span
class="ms-linksectionheader">


<h3
class="ms-standardheader">


<SharePoint:EncodedLiteral


ID="EncodedLiteral1"

runat="server"


Text="Admin page for setting custom preferences"

EncodeMethod='HtmlEncode'
/>


</h3>


</span>


</th>


</tr>


<tr>


<th
scope="row"
nowrap="nowrap">


<SharePoint:EncodedLiteral


ID="EncodedLiteral2"

runat="server"
Text="<% $Resources:wss,settings_siteurl %>"

EncodeMethod='HtmlEncode' />:


</th>


<td
dir="ltr">


<% SPHttpUtility.HtmlEncode(Web.Url + "/", Response.Output); %>


</td>


</tr>


</table>


</td>


</tr>


</table>


</td>


</tr>


<tr>


<td
valign="top"
style="padding: 4px 0px 4px 0px;"
height="100%">


<table
width="100%"
border="0"
cellspacing="0"
cellpadding="0"
class="ms-propertysheet">


<wssuc:InputFormSection Title="Global Settings"
id="ifmSQL"

Description="Logging and permissions for i5 users" runat="server">


<template_inputformcontrols>


<wssuc:InputFormControl LabelText="Domain (NETBIOS)" runat="server">


<Template_Control>


<wssawc:InputFormTextBox CssClass="ms-input"


ID="tbDomain" Runat="server" Columns="60"
/>


<wssawc:InputFormRequiredFieldValidator ID="reqDomain"

ControlToValidate="tbDomain"
Text="Error" runat="server"

ErrorMessage ="Required" EnableClientScript="true" Display="Dynamic">


</wssawc:InputFormRequiredFieldValidator>


</Template_Control>


</wssuc:InputFormControl>


<wssuc:InputFormControl LabelText="Error Logging" runat="server"

Description="Globally Enable Logging">


<Template_Control>


<SPSWC:InputFormCheckBox CssClass="ms-input"
ID="cbxLogging"

runat="server"
Text="Globally Enable Error Logging?"
/>


</Template_Control>


</wssuc:InputFormControl>


</template_inputformcontrols>


</wssuc:InputFormSection>


<wssuc:InputFormSection Title="Exchange Server Settings"
id="ifmExchange"

Description="Settings for communication with the Exchange Server" runat="server">


<template_inputformcontrols>


<wssuc:InputFormControl LabelText="Exchange Server OWA Address" runat="server">


<Template_Control>


<wssawc:InputFormTextBox CssClass="ms-input"


ID="tbExchangeServerHostname" Runat="server" Columns="60"
/>


<wssawc:InputFormRequiredFieldValidator ID="reqHostName"

ControlToValidate="tbExchangeServerHostname"
Text="Error"

runat="server" ErrorMessage ="Required"

EnableClientScript="true" Display="Dynamic">


</wssawc:InputFormRequiredFieldValidator>


</Template_Control>


</wssuc:InputFormControl>


</template_inputformcontrols>


</wssuc:InputFormSection>


<SPSWC:InputFormButtonSection runat="server">


<SPSWC:InputFormButtonAtBottom runat="server"
ID="cmdOK"
OnClick="OnClickOK"

TextLocId="Page_OkButton_Text"
/>


<SPSWC:InputFormButtonAtBottom runat="server"
ID="cmdCancel"
OnClick="OnClickCancel"

TextLocId="Page_CancelButton_Text" CausesValidation="false"
/>


</SPSWC:InputFormButtonSection>


</table>


</td>


</tr>


</table>

</asp:Content>

For now, we have an admin page with all required fields and controls but without any functionality because the OK button does not work and all user input is dicarded. It is time to get our SPProperties class back into play.

To get it working, we have to register the assembly by adding the following directive to the aspx file:

<%@ Register TagPrefix="SG" Namespace="SG.Global.SharePoint"

Assembly="Test.SPProperties, Version=1.0.0.0,

Culture=neutral, PublicKeyToken=fb3603850412338a"
%>

Make sure to adjust the Namespace and Assembly properties to fit your needs.

Next, we have to write some code to handle the button events and to load/store data. Whenever the page is called we use the Load event to retrieve all data, if any. Clicking on the OK button invokes the OnClickOK event handler which persists all user data to database. See below how this is done:

<script runat="server">

SPWeb Web;

 
 


protected override void
OnLoad(EventArgs e)
{

base.OnLoad(e);

Web = SPControl.GetContextWeb(Context);

 
 


if
(!Page.IsPostBack)
{

SPProperties settings =
new SPProperties(Web);

 
 

tbDomain.Text
= settings["Domain"];

cbxLogging.Checked
=


(String.IsNullOrEmpty(settings["Logging"])
?


false
:

Convert.ToBoolean(settings["Logging"]));

tbExchangeServerHostname.Text
= settings["ExchangeServerAddress"];

 
 

settings =
null;


}


}

 
 


protected
void OnClickOK(Object Sender, EventArgs e)
{

SPProperties settings =
new SPProperties(Web);

 
 

settings.SetValue("Domain", tbDomain.Text);

settings.SetValue("Logging", cbxLogging.Checked.ToString());

settings.SetValue("ExchangeServerAddress", tbExchangeServerHostname.Text);

settings =
null;

 
 

Response.Redirect("../Settings.aspx");


}

 
 


protected
void OnClickCancel(Object Sender, EventArgs e)
{

Response.Redirect("../Settings.aspx");


}

</script>

When we load the page for the first time after feature activation, no data is read and accessing the SPProperties internal StringDictionary returns String.Empty for non-existing keys. The OnClickOK event handler reads user input just as we are used to from traditional ASP.NET development. SPProperties' method SetValue takes two Strings: the first argument is the key to identify the slot in the collection and the second is the value being associated with the key. As soon as our work is done, we redirect to SharePoint's Settings.aspx page.

On page load, we obtain a reference to the current web and instantiate a new SPProperties object. All we have to do is parsing the values and assign them to the appropriate controls. Remember that only Strings are stored so that we have to utilize Convert.ToXXX in order to retrieve a boolean value for the checkbox.

Make sure that you did not forget to associate event handlers with the button controls by properly setting the OnClick property:

<SPSWC:InputFormButtonSection runat="server">

<SPSWC:InputFormButtonAtBottom runat="server"
ID="cmdOK"
OnClick="OnClickOK"

TextLocId="Page_OkButton_Text"
/>

<SPSWC:InputFormButtonAtBottom runat="server"
ID="cmdCancel"
OnClick="OnClickCancel"

TextLocId="Page_CancelButton_Text" CausesValidation="false"
/>

</SPSWC:InputFormButtonSection>

We're done! The last step involves provisioning the page as a feature.

Deploying the Page

Ideally, you would want to create a SharePoint Feature for your project which is responsible for deploying the page.

Creating a Feature

We need two files for the Feature: Feature.xml and Elements.xml. The first describes the Feature's characteristics, such as title or scope and the second file specifies actions and auxiliary files belonging to the Feature.

To create a Feature, make a new directory in ..\TEMPLATES\Features folder within the 12 hive. Give it a unique name like MyCustomAdminPage (alternatively, make a new sub folder 'Test' and keep your test Features there) and create a Feature.xml file within this directory:

<?xml
version="1.0"
encoding="utf-8"?>

<Feature
Id="4E35B4E5-D0E0-44c2-9BE4-F2160A3D476C"


Title="Custom Settings"


Description="Admin page for setting custom preferences"


Version="12.0.0.0"


Scope="Site"


xmlns="http://schemas.microsoft.com/sharepoint/">


<ElementManifests>


<ElementManifest
Location="Elements.xml"
/>


<ElementFile
Location="Pages/MyCustomSettings.aspx"/>


</ElementManifests>

</Feature>

Don't forget to create a new GUID to make sure it is globally unique. The ElementManifests container tells SharePoint that the details about the Feature are located in Elements.xml and that we want another file to be provisioned, namely MyCustomSettings.aspx

Next, create the Elements.xml file in the same Feature folder:

<?xml
version="1.0"
encoding="utf-8"?>

<Elements
xmlns="http://schemas.microsoft.com/sharepoint/">


<CustomAction


Id="MyPortal.MyCustomSettingsAction"


Location="Microsoft.SharePoint.SiteSettings"


GroupId="SiteCollectionAdmin"


Title="My Custom Settings"


Sequence="1001"


Rights="ManageWeb"


Description="Admin page for setting custom preferences"


ImageUrl="~sitecollection/_layouts/images/lg_ICCONFIG.gif">


<UrlAction Url ="~sitecollection/_layouts/__Test/MyCustomSettings.aspx"/>


</CustomAction>

</Elements>

Id is a free-form string which must be unique in the farm. Location tells SharePoint where we want the custom action to be applied, here: Microsoft.SharePoint.SiteSettings. Depending on the Location value GroupId specifies the column of the Site Settings page (Site Actions — Site Settings — Modify all Site Settings). The following groups exist by default in the SiteSettings area: UsersAndPermissions, Customization, QuickLaunch, Galleries, SiteAdministration and SiteCollectionAdmin.

For Location chose from one of these:

  • Microsoft.SharePoint.ContentTypeTemplateSettings
  • Microsoft.SharePoint.ContentTypeSettings
  • Microsoft.SharePoint.Administration.ApplicationCreated
  • Office.Server.ServiceProvider.Administration (Shared Services/SSP links)
  • Microsoft.SharePoint.ListEdit.DocumentLibrary
  • Microsoft.SharePoint.Workflows
  • NewFormToolbar
  • DisplayFormToolbar
  • EditFormToolbar
  • Microsoft.SharePoint.StandardMenu (SiteActions menu)
  • Mcrosoft.SharePoint.Create (_layouts/create.aspx - the screen used to specify what you want to create on your site)
  • Microsoft.SharePoint.ListEdit (the screen used to edit the properties of a list item)
  • EditControlBlock

The UrlAction element represents the URL for the link being displayed in the SiteCollectionAdmin group.

Please note that I usually put my test Features, controls and application pages in sub folders below TEMPLATE\Features, TEMPLATE\Layouts and TEMPLATE\ControlTemplates usually called __Test to keep all shortliving files in a single place. Please adjust the paths in the examples appropriately to meet your preferences.

Finally, we have to create our MyCustomSettings.aspx and place it in the Pages directory below the feature base dir just as described in the Location attribute in Feature.xml. Additionally copy MyCustomSettings.aspx in the TEMPLATE\Layouts folder on the web front-end if you don't plan to use a Solution for deployment.

Copy the complete Feature directory into the TEMPLATE\Features folder and invoke the following commands to install and activate the feature for you web. Open a console window and enter:

  1. cd C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN
  2. stsadm -o installfeature -name __Test/MyCustomAdminPage
  3. stsadm -o activatefeature -name __Test/MyCustomAdminPage -url http://dev.local
  4. iisreset /timeout:0

The url parameter is the URL of the site collection we want to activate the feature for. Navigate to yor web's settings page and locate you custom admin page to verify that everything has been deployed properly.

Summary

In this lesson you've learned two things:

  • how to facilitate the SPWeb.Properties data structure to securely store web-specific data in your web app's content database
  • how to create custom admin pages for your web applications.

The MOSS 2007 SDK documentation provides useful insights but don't expect any help about how to do things. Look out for fresh MSDN and TechNet articles instead. Anyhow, taking advantage of application pages in administrative areas is extremely helpful and a flexible, highly secure solution for persisting per-web information to the SharePoint content DB.

To get started download the code for the SPProperties class and the custom admin page: Source Code: SPProperties Article

Note: This is a Visual Studio 2008 Solution, get Microsoft Visual Studio 2008 Express from here.

Feel free to post your ideas to improve this article. Thanks for reading.