Wednesday, October 17, 2007

Custom Web service that operates within the context of WSS 3.0

Basic Steps for Creating a Web Service

  • Create an ASP.NET Web service in Microsoft Visual Studio 2005.
  • Create a class library within the Web service that defines the programming logic for the Web service.
  • Generate and edit a static discovery file and a Web Services Description Language (WSDL) file.
  • Deploy the Web service files to the _vti_bin directory.
  • Create a client application to consume the Web service.

Creating an ASP.NET Web Service in Visual Studio

  1. In Visual Studio click File, point to New, and then click Web Site.
  2. In the Templates box of the New Web Site dialog box, select ASP.NET Web Service, select File System in the Location box, choose a programming language and location at which to create the project, and then click OK.
  3. Within the new Web Service solution, create a separate class library project to contain the Web service logic. To create the project, click File, point to New, and then click Project.
  4. In the New Project dialog box, select Class Library in the Templates box, provide a name and location for the project, select Add to Solution in the Solution box, and then click OK.
  5. Add a reference to the System.Web.Services namespace in the class library project. Right-click the project in Solution Explorer, select System.Web.Services in the Add Reference dialog box, and click OK.
  6. Replace the default Class1.cs file in the project with the Service.cs file that Visual Studio provides in the App_Code folder of the Web Service.
    To replace the Class1.cs file with the Service.cs file
    In Solution Explorer, drag Service.cs to the top node in the class library project.
    Delete the Class1.cs file, and also delete the Service.cs file that remains in the App_Code folder.
  7. Open the Services.cs file and Replace:
    [WebService(Namespace = "http://tempuri.org/")]
    with:
    [WebService(Namespace = "http://schemas.microsoft.com/office/project/server/webservices/Service/", Name = "CusWebService", Description = "Contains the Service web service for Microsoft Project Server.")]

    using System;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using Microsoft.SharePoint;
    using Microsoft.SharePoint.Utilities;
     
    [WebService(Namespace = "http://schemas.microsoft.com/office/project/server/webservices/Service/", Name = "CusWebService", Description = "Contains the Service web service for Microsoft Project Server.")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class Service : System.Web.Services.WebService
    {
     public Service()
     {
     //Uncomment the following line if using designed components 
     //InitializeComponent(); 
     }
     
     [WebMethod]
     public string HelloWorld()
     {
     return "Hello World";
     }
     
     [WebMethod]
     public string GetSiteListCount()
     {
     SPWeb mySite = SPContext.Current.Web;
     SPListCollection lists = mySite.Lists;
     return mySite.Title + " contains " + lists.Count.ToString() + " web sites.";
     }
    }
  8. Create a strong name for the class library. In Solution Explorer, right-click the class library project, and in the Properties dialog box, click Signing, select Sign the assembly, and select <New> in the box for choosing a strong name key file.
  9. In the Create Strong Name Key dialog box, provide a file name for the key, deselect Protect my key file with a password, and click OK.
  10. To build only the class library project, right-click the project in Solution Explorer, and click Build.
  11. To add your assembly to the Global Assembly Cache (GAC), you can either drag and drop the assembly into the %windows%\assembly directory using 2 instances of Windows Explorer, or use the command line utility gacutil.exe that is installed with the .NET Framework SDK 2.0.
    To use gacutil.exe to copy the class library DLL into the GAC
    To open the Visual Studio command prompt, click Start, point to All Programs, point to Microsoft Visual Studio 2005, point to Visual Studio Tools, and click Visual Studio 2005 Command Prompt.
    At the command prompt type a command in the following form, and press ENTER:
    gacutile.exe -iF "<Full file system path to DLL>".
  12. Now you are ready to modify the assembly information in the default Service.asmx file of the Web service with information for the DLL from the GAC. To get information from the GAC, open the %windows%\assembly directory in Windows Explorer, right-click your assembly, and click Properties.
  13. To open Service.asmx in Solution Explorer, right-click the file and click Open.
  14. Remove the CodeBehind attribute from the page directive in Service.asmx, and modify the contents of the Class attribute so that the directive matches the following format, where MyServiceClass represents the class name that you provide in Service.cs, and the other values are taken from the Properties dialog box you opened in step 11:
    <%@ WebService Language="C#" Class="Service,CusWebServiceLib, Version=1.0.1.0, Culture=neutral, PublicKeyToken=6f06163942a3357e" %>
  15. Rename your .asmx file appropriately, and then save your changes.

Generating and Modifying Static Discovery and WSDL Files

  1. In Windows Explorer, copy the .asmx file of your Web service to \\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\LAYOUTS.
  2. Run Disco.exe at the command prompt from the LAYOUTS directory to generate .disco and .wsdl files. Run a command in the following format to generate the files in \LAYOUTS:
    disco http://MyServer/_layouts/MyCustomWebService.asmx
  3. To register namespaces of the Windows SharePoint Services object model, open both the .disco and .wsdl files and replace the opening XML processing instruction
    <?xml version="1.0" encoding="utf-8" ?>
    with instructions such as the following:
    <%@ Page="" Language="C#" Inherits="System.Web.UI.Page"%>
    <%@
    Assembly="" Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
    <%@
    Import="" Namespace="Microsoft.SharePoint.Utilities" %>
    <%@
    Import="" Namespace="Microsoft.SharePoint" %>
    <%
    Response.ContentType = "text/xml"; %>
  4. In the .disco file, modify the contract reference and SOAP address tags to be like the following example, which replaces literal paths with code generated paths through use of the Microsoft.SharePoint.Utilities.SPEncode class, and which replaces the method name that is specified in the binding attribute:
    <discovery xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        xmlns="http://schemas.xmlsoap.org/disco/">
    <
    contractRef ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request) + "?wsdl"),Response.Output); %> docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %> xmlns="http://schemas.xmlsoap.org/disco/scl/" />
    <
    soap address=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %> xmlns:q1="http://schemas.microsoft.com/projectserver/soap/Service/" binding="q1:ServiceSoap" xmlns="http://schemas.xmlsoap.org/disco/soap/" />
    <
    soap address=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %> xmlns:q2="http://schemas.microsoft.com/projectserver/soap/Service/" binding="q2:ServiceSoap12" xmlns="http://schemas.xmlsoap.org/disco/soap/" />
    </
    discovery>
  5. In the .wsdl file, make the following, similar substitution for the SOAP address that is specified:

    Replace:

    <soap:address location="http://litwareserver/_layouts/Service.asmx" />

    With:

    <soap:address location=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %> />

    And Replace:

    <soap12:address location="http://litwareserver/_layouts/Service.asmx" />

    With:

    <soap12:address location=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output); %> />

  6. Rename both files in the respective formats MyCustomWebServicedisco.aspx and MyCustomWebServicewsdl.aspx so that your service is discoverable through Windows SharePoint Services.

Copying the Web Service Files to the _vti_bin Directory

The _vti_bin virtual directory maps physically to the Local_Drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI directory, which contains the default Web service files used in Windows SharePoint Services. Copy the new MyCustomWebServicewsdl.aspx and MyCustomWebServicedisco.aspx files, as well as the MyCustomWebService.asmx file, to the ISAPI folder.

From the _vti_bin directory, a Web service offers its functionality to the site that is specified when adding a Web reference for the service.

To verify that your custom Web service is discoverable, navigate to http://litwareServer/_vti_bin/Service.asmx

Creating a Windows Application to Consume the Web Service

  1. Open Visual Studio 2005, and on the File menu, point to New, and then click Project.
  2. In the New Project dialog box, select Visual C# or Visual Basic, and then select the Windows Application template.
  3. Type a name for the application in the Name box, specify a location for the project files in the Location box, and then click OK.
  4. In Solution Explorer, right-click References, and then click Add Web Reference.
  5. In the address bar of the Add Web Reference browser, type the URL for the site to which to apply the service, as follows, and then press ENTER:
    http://litwareServer/_vti_bin/Service.asmx
  6. Click Add Reference to download the service contract for the Web service.
  7. using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Net;
     
    namespace WindowsApplication1
    {
     public partial class Form1 : Form
     {
     public Form1()
     {
     InitializeComponent();
     }
     
     private void button1_Click(object sender, EventArgs e)
     {
     litwareserver.CusWebService MyCustomService = new litwareserver.CusWebService();
     MyCustomService.Credentials = CredentialCache.DefaultCredentials;
     MessageBox.Show(MyCustomService.GetSiteListCount());
     }
     }
    }

Thursday, October 11, 2007

Move Images from MOSS to the File System

Background:

Images and Style Sheet files are all in the MOSS db when we deploy our feature. When the page loads, the images and style sheets are all retrieved from DB in real time. That means the page has to make a round trip to the DB each time in order to get those files. Thus, makes the page loading very slow.

Blobcache in the web.config can somehow solve this problem. But it's not stable. We experienced the slowness once in a while.

Solution:

Move the images and style sheets from the MOSS db to the file system. To do that, we will need to modify the solution package and redeploy it.

Comment out the file definitions in Feature.xml and Elements.xml.

Add the file definitions in Manifest.xml

Modify the master pages and style sheets to point the images to the file system.

Site Definition

A site definition is the top-level component in WSS that aggregates smaller, more modular
definitions to create a complete site template that can be used to provision sites.

A site definition itself does not represent a creatable site template. Instead, a site definition
contains one or more configurations, and these configurations are what appear to users as
creatable site templates. Therefore, the STS site definition contains three different
configurations: Team Site, Blank Site, and Document Workspace.

Site definitions are deployed within the 12\TEMPLATE\
SiteTemplates directory and are referenced in the 12\TEMPLATE\<culture>\XML directory in
WEBTEMP.XML files, where the <culture> folder is the locale identifier (12\TEMPLATE\
1033\XML for US English).

Web Template Files WEBTEMP.XML

<?xml version="1.0" encoding="utf-8"?>
<!-- _lcid="1033" _version="12.0.4518" _dal="1" -->
<!-- _LocalBinding -->
<Templates xmlns:ows="Microsoft SharePoint">
 <Template Name="GLOBAL" SetupPath="global" ID="0">
 <Configuration ID="0" Title="Global template" Hidden="TRUE" ImageUrl="" Description="This template is used for initializing a new site." > </Configuration>
 </Template>
 <Template Name="STS" ID="1">
 <Configuration ID="0" Title="Team Site" Hidden="FALSE" ImageUrl="/_layouts/images/stsprev.png" Description="A site for teams to quickly organize, author, and share information. It provides a document library, and lists for managing announcements, calendar items, tasks, and discussions." DisplayCategory="Collaboration" > </Configuration>
 <Configuration ID="1" Title="Blank Site" Hidden="FALSE" ImageUrl="/_layouts/images/blankprev.png" Description="A blank site for you to customize based on your requirements." DisplayCategory="Collaboration" AllowGlobalFeatureAssociations="False" > </Configuration>
 <Configuration ID="2" Title="Document Workspace" Hidden="FALSE" ImageUrl="/_layouts/images/dwsprev.png" Description="A site for colleagues to work together on a document. It provides a document library for storing the primary document and supporting files, a tasks list for assigning to-do items, and a links list for resources related to the document." DisplayCategory="Collaboration" > </Configuration>
 </Template>
 <Template Name="MPS" ID="2">
 <Configuration ID="0" Title="Basic Meeting Workspace" Hidden="FALSE" ImageUrl="/_layouts/images/mwsprev.png" Description="A site to plan, organize, and capture the results of a meeting. It provides lists for managing the agenda, meeting attendees, and documents." DisplayCategory="Meetings" > </Configuration>
 <Configuration ID="1" Title="Blank Meeting Workspace" Hidden="FALSE" ImageUrl="/_layouts/images/blankmwsprev.png" Description="A blank meeting site for you to customize based on your requirements." DisplayCategory="Meetings" > </Configuration>
 <Configuration ID="2" Title="Decision Meeting Workspace" Hidden="FALSE" ImageUrl="/_layouts/images/decisionmwsprev.png" Description="A site for meetings that track status or make decisions. It provides lists for creating tasks, storing documents, and recording decisions." DisplayCategory="Meetings" > </Configuration>
 <Configuration ID="3" Title="Social Meeting Workspace" Hidden="FALSE" ImageUrl="/_layouts/images/socialmwsprev.png" Description="A site to plan social occasions. It provides lists for tracking attendees, providing directions, and storing pictures of the event." DisplayCategory="Meetings" > </Configuration>
 <Configuration ID="4" Title="Multipage Meeting Workspace" Hidden="FALSE" ImageUrl="/_layouts/images/multipagemwsprev.png" Description="A site to plan, organize, and capture the results of a meeting. It provides lists for managing the agenda and meeting attendees in addition to two blank pages for you to customize based on your requirements." DisplayCategory="Meetings" > </Configuration>
 </Template>
 <Template Name="CENTRALADMIN" ID="3">
 <Configuration ID="0" Title="Central Admin Site" Hidden="TRUE" ImageUrl="" Description="A site for central administration. It provides Web pages and links for application and operations management." > </Configuration>
 </Template>
 <Template Name="WIKI" ID="4">
 <Configuration ID="0" Title="Wiki Site" Hidden="FALSE" ImageUrl="/_layouts/images/wikiprev.png" Description="A site for a community to brainstorm and share ideas. It provides Web pages that can be quickly edited to record information and then linked together through keywords" DisplayCategory="Collaboration" > </Configuration>
 </Template>
 <Template Name="BLOG" ID="9">
 <Configuration ID="0" Title="Blog" Hidden="FALSE" ImageUrl="/_layouts/images/blogprev.png" Description="A site for a person or team to post ideas, observations, and expertise that site visitors can comment on." DisplayCategory="Collaboration" > </Configuration>
 </Template>
</Templates>

Site Definition Project Schema Sample ONET.xml

<?xml version="1.0" encoding="utf-8"?>
<Project Title="My Site Definition"
Revision="0"
ListDir="Lists"
xmlns:ows="Microsoft SharePoint"
xmlns="http://schemas.microsoft.com/sharepoint/">
 <NavBars>
 <NavBar Name="Site Pages"
 Prefix="&lt;table border=0 cellpadding=4 cellspacing=0&gt;"
 Body="&lt;tr&gt;&lt;td&gt;&lt;table border=0 cellpadding=0 cellspacing=0&gt;&lt; 

    
tr&gt;&lt;td&gt;&lt;img src='/_layouts/images/blank.gif' ID='100' alt='' border=
0&gt;&amp;nbsp;&lt;/td&gt;&lt;td valign=top&gt;&lt;a ID=onetleftnavbar#LABEL_ID# href=
      
'#URL#'&gt;#LABEL#&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;"
Suffix=
"&lt;/table&gt;"
 ID="1028" >
 <NavBarLink Name="Web Page 1" Url="SitePages/Page01.aspx" />
 <NavBarLink Name="Web Page 2" Url="SitePages/Page02.aspx" />
 <NavBarLink Name="Web Page 3" Url="SitePages/Page03.aspx" />
 <NavBarLink Name="Web Page 4" Url="SitePages/Page04.aspx" />
 </NavBar>
 </NavBars>
 <DocumentTemplates />
 <Configurations>
 <Configuration ID="-1" Name="NewWeb" />
 <Configuration ID="0" Name="Default">
 <Lists>
 <!-- Adds the Litware Vendor List -->
 List FeatureId="FBDECD96-62DC-48c8-8F0A-7B827A042FD9" Type="10001"
 Title="Litware Vendors"
 Url="Lists/Vendors" QuickLaunchUrl="Lists/Vendors/AllItems.aspx" />
 <!-- Adds the Litware Customer List -->
 List FeatureId="FBDECD96-62DC-48c8-8F0A-7B827A042FD9" Type="10002"
 Title="Litware Customers"
 Url="Lists/Customers" QuickLaunchUrl="Lists/Customers/AllItems.aspx" />
 <List FeatureId="00BFEA71-D1CE-42de-9C63-A44004CE0104" Type="104"
 Title="Announcements"
 Url="$Resources:core,lists_Folder;/$Resources:core,announce_Folder;">
 <Data>
 <Rows>
 <Row>
 <Field Name="Title">
 $Resources:Litware,DefaultAnnoucementTitle;
 </Field>
 <Field Name="Body">
 $Resources:Litware,DefaultAnnoucementBody;
 </Field>
 <Field Name="Expires">&lt;ows:TodayISO/&gt;</Field>
 </Row>
 </Rows>
 </Data>
 </List>
 </Lists>
 <Modules>
 <Module Name="Default" />
 </Modules>
 <SiteFeatures>
 <!-- The Basic Web Parts feature installs the WSS Web Part entries. -->
 <Feature ID="00BFEA71-1C5E-4A24-B310-BA51C3EB7A57" />
 </SiteFeatures>
 <WebFeatures>
 <!-- The Team Collaboration feature includes the basic site lists. -->
 <Feature ID="00BFEA71-4EA5-48D4-A4AD-7EA5C011ABE5" />
 </WebFeatures>
 </Configuration>
 </Configurations>
 <Modules>
 <Module Name="Default" Url="" >
 <File Url="default.aspx" NavBarHome="True" Type="Ghostable"></File>
 </Module>
 <Module Name="Default" Url="" SetupPath="sitetemplates\sts">
 <File Url="default.aspx" NavBarHome="True" Type="Ghostable">
 <View List="$Resources:core,lists_Folder;/ 

    
$Resources:core,announce_Folder;"
BaseViewID="0" WebPartZoneID="Left" WebPartOrder="0" />
 <View List="Lists/Vendors" BaseViewID="0" WebPartZoneID="Left" WebPartOrder="1" />
 <View List="Lists/Customers" BaseViewID="0" WebPartZoneID="Left" WebPartOrder="2" />
 </File>
 </Module>
 <Module Name="Default" Url="" SetupPath="features\customsitepages">
 <File Url="default.aspx"
 NavBarHome="True"
 Type="Ghostable"
 Path="PageTemplates\Page01.aspx" />
 <File Url="topsites.aspx" Type="GhostableInLibrary" >
 <Property Name="PublishingPageLayout" Value="~SiteCollection/_catalogs/masterpage/tabviewpagelayout.aspx, $Resources:spscore,TabViewPageLayoutTitle;" />
 <Property Name="Title" Value="$Resources:spscore,SiteDirectoryTopSitesPageTitle;" />
 <Property Name="Comments" Value="$Resources:spscore,SiteDirectoryTopSitesPageDescription;" />
 <AllUsersWebPart WebPartZoneID="Header" WebPartOrder="1">
 <![CDATA[
 <WebPart xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/WebPart/v2">
 <Title>$Resources:spscore,ReportCenterOnet_CWP_Title</Title>
 <FrameType>TitleBarOnly</FrameType>
 <Description>$Resources:spscore,ReportCenterOnet_CWP_Description</Description>
 <IsIncluded>true</IsIncluded>
 <ZoneID>TopLeftZone</ZoneID>
 <PartOrder>1</PartOrder>
 <FrameState>Normal</FrameState>
 <Height />
 <Width />
 <AllowRemove>true</AllowRemove>
 <AllowZoneChange>true</AllowZoneChange>
 <AllowMinimize>true</AllowMinimize>
 <IsVisible>true</IsVisible>
 <Hidden>false</Hidden>
 <DetailLink />
 <HelpLink />
 <Dir>Default</Dir>
 <PartImageSmall />
 <MissingAssembly />
 <PartImageLarge>/_layouts/images/wp_pers.gif</PartImageLarge>
 <IsIncludedFilter />
 <Assembly>Microsoft.SharePoint.Portal, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c</Assembly>
 <TypeName>Microsoft.SharePoint.Portal.WebControls.ContactFieldControl</TypeName>
 </WebPart>
 ]]>
 </AllUsersWebPart>
 <AllUsersWebPart WebPartZoneID="TopZone" WebPartOrder="1">
 <![CDATA[
 <webParts>
 <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">
 <metaData>
 <type name="Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart,Microsoft.SharePoint.Publishing,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" />
 <importErrorMessage>$Resources:spscore,WPImportErrorMessage;</importErrorMessage>
 </metaData>
 <data>
 <properties>
 <property name="Title" type="string">$Resources:spscore,TopSitesWebpartTitle;</property>
 <property name="Description" type="string">$Resources:spscore,TopSitesWebpartDescription;</property>
 <property name="ChromeType" type="chrometype">None</property>
 <property name="WebUrl" type="string">~Site</property>
 <property name="ListName" type="string">$Resources:spscore,SitesOnet_SitesList_Title;</property>
 <property name="ItemStyle" type="string">Bullets</property>
 <property name="XslLink" type="string">~SiteCollection/Style Library/XSL Style Sheets/ContentQueryMain.xsl</property>
 <property name="QueryOverride" type="string">&lt;Where&gt;&lt;Eq&gt;&lt;FieldRef Name="TopSite" /&gt;&lt;Value Type="Boolean"&gt;1&lt;/Value&gt;&lt;/Eq&gt;&lt;/Where&gt;</property>
 <property name="CommonViewFields" type="string">URL,URL;Description,Note</property>
 <property name="BaseType" type="string">0</property>
 <property name="DataColumnRenames" type="string">URL,LinkUrl;Description,LinkToolTip</property>
 <property name="FilterField1" type="string">{fdc3b2ed-5bf2-4835-a4bc-b885f3396a61}</property>
 <property name="FilterOperator1" type="Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart+FilterFieldQueryOperator,Microsoft.SharePoint.Publishing,Version=12.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c">Eq</property>
 <property name="FilterValue1" type="string">Approved</property>
 </properties>
 </data>
 </webPart>
 </webParts>]]>
 </AllUsersWebPart>
 </File>
 </Module>
 </Modules>
</Project>