Wednesday, February 27, 2008

Creating an AJAX Navigation Control for Windows SharePoint Services 3.0

 

Requirements

Download ASP.NET AJAX 1.0 from http://www.microsoft.com/downloads/details.aspx?FamilyID=ca9d90fa-e8c9-42e3-aa19-08e2c027f5d6&displaylang=en

Concepts

Read an overview of the new features in ASP.NET 2.0 http://quickstarts.asp.net/QuickStartv20/aspnet/doc/tipstricks/default.aspx

An exciting new feature for controls are Client-side CallBacks, which allow a control to execute an out-of-band request back to the server to obtain additional data, without posting the entire page. This feature relies on browser support for callback handling (usually through XMLHTTP), as specified by the SupportsClientCallbacks property in browser capabilities.

The TreeView control takes advantage of this feature to enable populating tree nodes on-demand when a parent node is expanded on the client. GridView and DetailsView also take advantage of this feature to implement paging and sorting when EnableSortingAndPagingCallbacks is set to true. A control registers itself as being able to receive callback events by implementing the ICallbackEventHandler interface. This interface allows the page to invoke registered delegates for returning callback data to the client. There are two methods of the ICallBackHandler interface, RaiseCallbackEvent and GetCallbackResult. RaiseCallbackEvent accepts an argument for context (args passed from the client), which can be used to process the event. GetCallbackResult returns a String that represents the data to return to the client. The reason this interface is separated into two methods is to allow for asynchronous processing, for example to retrieve data from a data source control. The control then registers a client-side callback function that knows how to create the arguments to pass to the server-side delegate, and an error handling function to be invoked when an error in callback processing occurs. Finally, the control emits references to callback events using Page.ClientScript.GetCallBackEventReference. An example below demonstrates the use of client-side callbacks to implement a cascading DropDownList scenario. In this case, the Page itself implements the ICallbackEventHandler interface, for demonstration purposes.

Check the Partial Page Rendering Overview at http://msdn2.microsoft.com/en-us/library/bb386573.aspx

Check Implementing Client Callbacks Programmatically Without Postbacks in ASP.NET Web Pages at http://msdn2.microsoft.com/en-us/library/ms178208.aspx

Create the Solution

Create a Visual Studio solution containing two projects:

·        AjaxNavigation (SharePoint Solution)

·        AjaxNavigationTestClient (Web Site)

The idea is to develop and test the AJAX User Control in the AjaxNavigationTestClient and the copy the control to the SharePoint Solution so it can be tested and deployed in SharePoint.

Registering a Site Map Provider

Register a Site Map Provider named SPNavigationProvider in the AjaxNavigationTestClient web.config:

        <siteMap defaultProvider="CurrentNavSiteMapProvider" enabled="true">

              <providers>

                    <add name="CurrentNavSiteMapProvider" type="System.Web.XmlSiteMapProvider" siteMapFile="Web.sitemap" />                   

              </providers>

        </siteMap>

Add a new Web.sitemap file in the root of AjaxNavigationTestClient:

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

<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >

    <siteMapNode url="/Default.aspx" title="Home Page"  description="">

            <siteMapNode url="/Page1.aspx" title="Page 1"  description="">

                  <siteMapNode url="/SubPage1a.aspx" title="Sub Page 1a"  description="" />

                  <siteMapNode url="/SubPage1b.aspx" title="Sub Page 1b"  description="" />

            </siteMapNode>

            <siteMapNode url="/Page2.aspx" title="Page 1"  description="">

                  <siteMapNode url="/SubPage2a.aspx" title="Sub Page 2a"  description="" />

                  <siteMapNode url="/SubPage2b.aspx" title="Sub Page 2b"  description="" />

            </siteMapNode>

    </siteMapNode>

</siteMap>

Creating the User Control

Create a User Control and name it AjaxNavigation.ascx.

Drop a TreeView Control, open TreeView Tasks and select New Data Source. Create a new Site Map Data Source and set the SiteMapProvider property to the name of the site map provider to use (SPNavigationProvider, as shown in the previous step).

Apply skinning to the TreeView control using the out-of-the-box skinning features.

Copy the User Control to the SharePoint Solution

Copy the AjaxNavigation.ascx to SharePoint Solution folder /TEMPLATE/CONTROLTEMPLATES

Copy the AjaxNavigation.ascx.cs to SharePoint Solution folder /CODE

Modify the control declaration, so the .ascx can use appropriate inherited class (check the full assembly name for AjaxNavigation.dll in the GAC)

<%@ Control Language="C#" AutoEventWireup="true" Inherits="AjaxNavigation.UserControls_AjaxNavigation,AjaxNavigation,Version=1.0.0.0,Culture=neutral,PublicKeyToken=ef72d5cefb9f703d" %>

<%@ Import Namespace="AjaxNavigation" %>

Modify the ManifestTemplate.xml:

<Assembly DeploymentTarget="GlobalAssemblyCache" Location="AjaxNavigation.dll">

Deploy the Solution

Modify the deploy.cmd script as:

In the beginning of the script, insert:

@Set Path=C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN;%PATH%

Modify:

SET SITE_URL="http://www.myPublishingPortal.local"

Register an external tool in Visual Studio named “Deploy SharePoint Solution” with the following parameters:

Command:         $(ProjectDir)\DeploymentScripts\deploy.cmd

Initial Directory: $(ProjectDir)

Use Output Window

Select Tools > Deploy SharePoint Solution

Use the control in a Master Page or Page Layout

Using SharePoint Designer or Visual Studio, add the following lines to a Master Page or Page Layout:

<%@ Register Src="/_controltemplates/AjaxNavigation.ascx" TagName="AjaxNavigation" TagPrefix="my" %>

<my:AjaxNavigation ID="AjaxNavigation1" runat="server" />

Test the Master Page or Page Layout.

More Information

To develop a AJAX enabled Web Part – instead of an User Control – check the Walkthrough: Creating a Basic ASP.NET AJAX-enabled Web Part at http://msdn2.microsoft.com/en-us/library/bb861877.aspx

More details on SharePoint and AJAX integration at http://sharepoint.microsoft.com/blogs/mike/Lists/Posts/Post.aspx?ID=3   

 

Creating SharePoint Publishing Pages Programmaticaly

Technorati Tags:

Creating and editing publishing pages in SharePoint is a task that can be automated using the SharePoint Object Model.

A common scenario for this is a content migration tool that picks content items from any legacy content management system and automatically creates or updates the corresponding pages in a SharePoint publishing site.

Is this example, the migration tool reads a Microsoft Access Database containing a table named ContentItems and creates a new page for each record, mapping the database table columns into page fields in the publishing page.

 

ContentItems

 

The resulting pages should look like this...

 

PublishingPage

 

Migration Utility (command line)

 

{

            string connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=..\\..\\Database\\Content.accdb;Persist Security Info=False";

            string siteUrl = "http://www.myPublishingSite.local";

            string pageLayoutFile = "ArticleLeft.aspx";

            string pageNamePrefix = "NewPage";

           

            ContentMigrationLib.ContentDatabase instance = new ContentMigrationLib.ContentDatabase(connectionString);

            instance.UploadContent(siteUrl, pageLayoutFile, pageNamePrefix);

        }

    }

 

The ContentDatabase class implementation is:

 

ContentMigrationLib

 

using System;

using System.Data;

using System.Data.OleDb;

using System.Collections.Generic;

using System.Text;

using System.Collections;

using System.Collections.Specialized;

using Microsoft.SharePoint;

using Microsoft.SharePoint.Publishing;

using Microsoft.SharePoint.Publishing.Fields;

 

namespace ContentMigrationLib

{

    public class ContentDatabase

    {

        // references

        // http://msdn2.microsoft.com/en-us/library/ms493244.aspx

        // http://mindsharpblogs.com/aaron/

        // http://msdn2.microsoft.com/en-us/library/microsoft.sharepoint.publishing.publishingpage.fields.aspx

        private string m_connectionString;

 

        public ContentDatabase(string connectionString)

        {

            m_connectionString = connectionString;

        }

 

        public void UploadContent(string siteUrl, string pageLayoutFile, string pageNamePrefix)

        {

            //get content items from database

            OleDbConnection connection = new OleDbConnection(m_connectionString);

            connection.Open();

            OleDbCommand command = new OleDbCommand("SELECT ID, Title, ByLine, [Page Content] FROM ContentItems", connection);

            OleDbDataReader reader = command.ExecuteReader();

          

            using (SPSite site = new SPSite(siteUrl))

            {

                //pick the page layout to use

                PublishingSite publishingSite = new PublishingSite(site);

                PageLayoutCollection pageLayouts = publishingSite.GetPageLayouts(false);

                string serverRelativeUrl = "/_catalogs/masterpage/" + pageLayoutFile;

                PageLayout layout = pageLayouts[serverRelativeUrl];

                          

                //for each content item

                while (reader.Read())

                {

                    int id = reader.GetInt32(0);

 

                    Dictionary<string,string> fields = new Dictionary<string,string>();

                    fields.Add("Title", reader.GetString(1));

                    fields.Add("Byline", reader.GetString(2));

                    fields.Add("Page Content", reader.GetString(3));

                    string pageName = String.Format("{0}{1}.aspx", pageNamePrefix, id.ToString());

 

                    //create the publishing page

                    CreateNewPage(site.RootWeb, layout, pageName, String.Empty, fields);

                }

            }

            connection.Close();

        }

 

        public void CreateNewPage(SPWeb web, PageLayout pageLayout, string newPageName, string checkInComment, Dictionary<string, string> fields)

        {

            // Validate the input parameters.

            if (null == web)

            {

                throw new System.ArgumentNullException("web");

            }

            if (null == pageLayout)

            {

                throw new System.ArgumentNullException("pageLayout");

            }

 

            // Get the PublishingWeb wrapper for the SPWeb that was passed in.

            PublishingWeb publishingWeb = null;

            if (PublishingWeb.IsPublishingWeb(web))

            {

                publishingWeb = PublishingWeb.GetPublishingWeb(web);

            }

            else

            {

                throw new System.ArgumentException("The SPWeb must be a PublishingWeb", "web");

            }

          

            // Create the new page in the PublishingWeb.

            PublishingPageCollection pages = publishingWeb.GetPublishingPages();

            PublishingPage newPage = pages.Add(newPageName, pageLayout);

           

            foreach (string fieldName in fields.Keys)

                newPage.ListItem[fieldName] = fields[fieldName];

            

            newPage.Title = fields["Title"];

            newPage.Update();

 

            // Check in the new page so that others can work on it.

            newPage.CheckIn(checkInComment);         

        }

    }

}

Tuesday, September 11, 2007

Intellisense

To get intellisense while editing SharePoint XML configuration files, just copy the WSS.XSD schema to Visual Studio schemas folder.

XCOPY "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\XML\WSS.XSD" "C:\Program Files\Microsoft Visual Studio 8\Xml\Schemas"

Wednesday, August 29, 2007

How to copy files across multiple Remote Desktop sessions

This approach does not require exposing the local drives in the remote machine. It also works across multiple remote desktop sessions.

(1) On the remote computer, create a ZIP file containing the files to be copied

(2) On the remote computer, open WordPad, paste the ZIP file into the WordPad document, and copy the ZIP file in the body of the document

(3) On the local computer, open WordPad and paste the ZIP file

You can switch the local and remote roles in this example.


 

Monday, July 9, 2007

Generate .NET classes with XSD.EXE based on schemas with import declarations

If you define two schemas - MySchema and MySubSchema - where MySchema references MySubschema, you will probably get in trouble if you try to generate .NET classes for those schemas using XSD.EXE.

Here's a quick example: MySchema.xsd is imports MySubschema.xsd and declares an element of type ns2:MySubSchemaRoot

MySchema.xsd

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

<xs:schema xmlns:b="http://schemas.microsoft.com/BizTalk/2003" xmlns="http://Namespace1" xmlns:ns2="http://Namespace2" targetNamespace="http://Namespace1" xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:import schemaLocation=".\MySubSchema.xsd" namespace="http://Namespace2" />


<xs:annotation>

<xs:appinfo>

<b:references>

<b:reference targetNamespace="http://Namespace2" />

</b:references>

</xs:appinfo>

</xs:annotation>

<xs:element name="MySchemaRoot">

<xs:complexType>

<xs:sequence>

<xs:element name="Element1" type="xs:string" />


<xs:element ref="ns2:MySubSchemaRoot" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:schema>


 

MySubSchema.xsd

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

<xs:schema xmlns:b="http://schemas.microsoft.com/BizTalk/2003" xmlns="http://Namespace2" targetNamespace="http://Namespace2" xmlns:xs="http://www.w3.org/2001/XMLSchema">

<xs:element name="MySubSchemaRoot">

<xs:complexType>

<xs:sequence>

<xs:element name="Element1" type="xs:string" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:schema>


 

If you run XSD.EXE to generate the .NET class for MySchema…

xsd.exe /c MySchema.xsd

…you'll get this error:

The element 'http://Namespace2:MySubSchemaRoot' is missing.

To workaround this issue, you must also specify MySubSchema.xsd as a parameter for xsd.exe, like this:

xsd.exe /c MySubSchema.xsd MySchema.xsd

This will correctly generate MySchema.cs class

Thursday, July 5, 2007

How to Create a Site Layout Feature

From a simple perspective, customizing a SharePoint Internet Publishing Site means creating new page layouts (templates) based on customized master pages. These page layouts and master pages use many resources, such as style sheets, images, …

The Web Designer may start by creating a static HTML storyboard of your site. Then, he will use SharePoint Designer to integrate the static HTML into customized master pages and page layouts, merging the HTML with the server controls required by SharePoint. Some examples of minimal master pages can be found at http://msdn2.microsoft.com/en-us/library/aa660698.aspx . The customized master pages and page layouts created in SharePoint designer will be stored in the site's Master Page Gallery located at /_catalogs/masterpages of the site.

Meanwhile, developers may be busy programming services and components for the site, such as a sophisticated search control, a workflow tracking page or a Virtual Earth mapping web part. Assuming that each development team member (including the designer) has its own stand-alone development environment, there will come a time when the customizations made by the designer must be integrated with those made by the developers.

For the first time "environment synchronization", the designer may just export its Site to a CAB file that should be imported in each developer workstation. But for subsequent synchronizations, it should be far better to just pack the designer's customizations - not the site itself - into a package, and simply deploy that package on each developer workstation. The same approach should be used to publish design updates to a production site.

In fact, the customized design should be seen as a package that could be deployed and applied to any SharePoint site. It should not be just a set of master pages, page layouts, styles and images stored in the libraries (/_catalogs/masterpages, /Style Library, …) of a specific site. Here's when SharePoint Features come in to place.


 

Like Web Parts, Workflows or just custom Web Forms, SharePoint Web Design Customizations can be packed into SharePoint Feature. Take a look at the C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\PublishingLayouts folder of your SharePoint environment. There you have a good example of a feature created to pack design customizations, including master pages, page layouts, styles and images.

So, to pack your design customization into a SharePoint Feature called MyLayouts, just follow this checklist:

  1. Create the feature files

Bellow C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES, create the following folder tree:

MyLayouts

Images

Styles

MasterPages

PageLayouts

Copy your customized masters pages, page layouts, images and styles to each corresponding folder.

Also copy to MyLayouts folder the Feature.xml and ProvisionedFiles.xml files (use the samples from C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\PublishingLayouts)


 

  1. Edit Feature.xml and ProvisionedFiles.xml

Create a new GUID and place it on the Feature.xml file.

Edit the ProvisionedFiles.xml, and reference all your components.

For example, if you have created a GIF file MyImage1.gif, add it to the Images module section. Notice that the GIF should be referenced as "images/MyImage1.gif" in the MyStyleSheet1.css, or as "/StyleLibrary/Images/MyImage1.gif" from the page layouts or master pages.

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

<Module Name="MyMasterPages" Url="_catalogs/masterpage" Path="MasterPages" RootWebOnly="TRUE">

<File Url="My.master" Type="GhostableInLibrary">

<Property Name="ContentType" Value="My Main Master Page" />

<Property Name="MasterPageDescription" Value="My Main Master Page Description" />

</File>


</Module>

<Module Name="Images" Url="Style Library/Images" Path="Images" RootWebOnly="TRUE">

<File Url="MyImage1.gif" Name="MyImage1.gif" Type="GhostableInLibrary" />


</Module>

<Module Name="Styles" Url="Style Library" Path="Styles" RootWebOnly="TRUE">

<File Url="MyStyleSheet1.css" Type="GhostableInLibrary" />


</Module>

</Elements>


 

  1. Install and activate the MyLayouts Feature

@set Path=C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN;%PATH%

stsadm -o installfeature -filename MyLayouts\feature.xml

stsadm -o activatefeature -filename MyLayouts\feature.xml –url http://www.yoursite.local


 


 

From now on, your design components live in the file system, at C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\MyLayouts, but are also available in the SharePoint site collection http://www.yoursite.local in libraries such as /Style Library and /_catalogs/masterpage. SharePoint "ghosts" those files because of the GhostableInLibrary attribute used in ProvisionedFiles.xml. To update your design components, just edit the files in the file system a do an IIS Recycle.

Finally, to pack and deploy your design customizations to other developer's environment, you just have to copy the folder C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\MyLayouts and install and activate the feature on the target machine. Simple?

Wednesday, July 4, 2007

How to Automate SharePoint Site Provisioning

SharePoint Solutions can be packed in a CAB file using the STSADM tool, as described in a previous post.

Whenever you need deploy or redeploy a solution on a particular environment, you have to do a bit more than just run STSADM again. Particularly, you have to:

You want to completely automate these steps and create a full provisioning process, using a script such as this:

ECHO OFF

Set cabFileName=YourSiteDefinition.CAB

set SiteName=http://www.yoursite.local

set siteOwnerEmail=administrator@yoursite.local

set siteOwnerLogin=%computername%\administrator

@set Path=C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN;%PATH%

ECHO ON


 

stsadm -o deletesite -url %siteName%


 

@Set Path=C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\BIN;%PATH%

stsadm -o createsite -url %siteName% -sitetemplate BLANKINTERNET -ownerlogin %siteOwnerLogin% -owneremail %siteOwnerEmail%


 

stsadm.exe -o import -url %siteName% -includeusersecurity -filename %cabFileName%


 

Creating and saving this script in your Source Control server, along with the .CAB files containing your Site Definitions, allows the developer to quickly setup and provision the solution in the local development environment, as well publishing updates to test, staging and production environments.

Installing additional components

Probably, your Site Definition may use specific components, such as User Controls, Web Forms, Mobile User Forms and so on. The deployment of these components can also be automated, using a batch:

Set SharepointHive= "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE"

Set MobileControlDir=%SharepointHive%\LAYOUTS\MOBILE

Set UserControlDir=%SharepointHive%\CONTROLTEMPLATES

Set MyAssembly= MyUserControls.dll


 

xcopy /Y usercontrols\*.* %UserControlDir%

xcopy /Y mobilecontrols\*.* %MobileControlDir%


 

GACUtil /i %MyAssembly% /f


 

Going further

This approach works fine, but more complex solutions may require a more sophisticated deployment process, using WSP Solution packages. A good subject for a future post.


 

Contributors: Telmo Cruz