Creating a link-enabled document library using Windows SharePoint Services and .NET 2.0

Joris Poelmans
Dolmen

Applies to:
  • Windows SharePoint Services
  • Visual Studio .NET 2005
  • .NET Framework 2.0
Summary:

An interesting customisation for Windows SharePoint Services (WSS) is the ability to track relationships between documents. In this article we will create an extension for WSS which will allow users to link documents to each other. This extension will be written in .NET 2.0 code to demonstrate how you can use Visual Studio 2005 to enhance your SharePoint sites. To be able to create a document linking solution on WSS with ASP.NET 2.0, we will need to complete a number of steps: 1. Upgrading Windows SharePoint Services to ASP.NET 2.0 2. Creating a custom SharePoint list to store the relationships between the documents 3. Extend the SharePoint document library context menu 4. Add your custom administration page for linking documents

Contents:

Step 1: Upgrading Windows SharePoint Services to ASP.NET 2.0

With the release of Windows SharePoint Services Service Pack 2 (WSS SP2), it is possible to use .NET 2.0 code in your SharePoint applications. After installing WSS SP2 you should upgrade your WSS sites to be able to use the .NET 2.0 framework. Run stsadm.exe from command line:

stsadm.exe –o upgrade –forceupgrade –url http://URLOftheVirtualServer}
This operation will make some changes to the web.config to facilitate for changes in the ASP.NET 2.0 security model and to disable ASP.NET 2.0 event validation. The –forceupgrade option will also upgrade the schema of the attached content databases.

After upgrading your WSS sites you will be able to change the version of ASP.NET of SharePoint by following these steps:
1. Click Start, click Run, type inetmgr and then click OK 2. In the Internet Information Services (IIS) Manager console, expand the nodes until the SharePoint virtual server is visible under the Web Sites node. 3. Right-click the Web application, and then click Properties. 4. On the Properties page, click the ASP.NET tab, and then change the version of ASP.NET to 2.0



Attention: Although you can use .NET 2.0 for WSS after installing Service Pack 2, SharePoint Portal Server (SPS) must still be configured to use .NET 1.1. If you want to run both SPS and WSS with .NET 2.0 on the same server, you should create a separate virtual server for WSS and extend it. To do this, complete the following steps:

  • Click Start, Click Run, type inetmgr and then click OK

  • In the Internet Information Services (IIS) Manager console, expand the Application Pools node and create a new application pool, e.g. WSSAppPool. You must run WSS in a separate application pool because it is not possible to run two different versions of ASP.NET in the same IIS process

  • Right-click the Web Sites node and select Create New Web Site, follow the wizard to complete site creation

  • Open the SharePoint Central Administration page and click Extend or upgrade virtual server

  • On the Virtual Server List page, click the name of the newly created virtual server

  • On the Extend Virtual Server page, in the Provisioning Options section, select Extend and create a content database

  • Select the existing application pool that you created in the previous steps, WSSAppPool

  • Specify other options and click OK

  • Make sure that after extending your virtual server, the _layouts, _vti_bin and _wpresources also use the

  • Create a new top-level site

  • Upgrade the WSS virtual server as described in the previous section


Step 2: Creating a custom SharePoint list to store relationships between documents

When you need to make changes to SharePoint site definitions, it is considered best practice to copy an existing default SharePoint site definition. (For more information see Supported and unsupported scenarios for working with custom site definitions) We are going to create a copy of the default SharePoint teamsite site definition and alter it by adding in a custom list called DocLinks.

  1. Navigate to the following directory on your SharePoint server:
    Drive:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\1033

  2. Create a copy of the STS site definition folder and paste it in the same directory (Drive:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\1033)

  3. 3. Rename the folder containing the site definition files to STSDOCLINKSLIST

Next you need to make sure that your new site definition is available from the Template Selection page. To accomplish this you need to create an additional WEBTEMP.XML file.

1. Make a copy of the WEBTEMP.XML file located at Local_Drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\60\TEMPLATE\1033\XML.. Rename the file to WEBTEMPSTSDOCLINKS.XML

2. Open WEBTEMPSTSDOCLINKS.XML and delete all the code between the TEMPLATES tags

3. Copy the code listed below between the TEMPLATES tags

<Template Name="STSDOCLINKS" ID="10002">
  <Configuration ID="0" Title="Project Site with document linking" TYPE="0"
    Hidden="FALSE" 
    ImageUrl="/_layouts/images/stsprev.png" 
    Description="This template provides a project site with document linking."> 
  </Configuration>
</Template>
4. The Name attribute must contain the same name, in all capital letters, that is assigned to the new folder. Also, in order to avoid conflict with IDs already used in Windows SharePoint Services, use unique values greater than 10,000 for the ID attribute.

5. Save the XML file

6. Reset Internet Information Services (IIS) for the new template to appear as an option on the Template Selection page. Go to Start>Run. Type iisreset.

7. Create a new site based on the newly created site definition

A site definition contains a list definition for each type of list (for example, announcements, tasks, document libraries, and so on) so the easiest way to create a custom list definition is to start from an existing list definition.

The files used in a list definition are found in one of the subfolders of Drive:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\Locale_ID\Site_Definition\LISTS. Each folder listed in here contains a set of files:

  • A SCHEMA.XML file that defines the contents of list and item views

  • An AllItems.aspx file that provides the page for viewing the list

  • NewForm.aspx, EditForm.aspx, and DisplayForm.aspx files that provide pages for working with individual list items

Start creating a new list definition by creating a copy of the Custom List list definition:

1. Navigate to the following directory on your SharePoint server in the custom site definition folder you just created: Drive:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\1033\STSDOCLINKS\LISTS

2. Make a copy of the CUSTLIST list definition and paste it into the same directory

3. Rename the folder to DOCLINKSLIST

Within the list definition folder, you will find the ASP.NET and XML files which provide the functionality necessary to view and edit the list. In this same directory you will also find a XML file named SCHEMA.XML. This file contains the full definition for your list.

1. In the DOCLINKSLIST list definition directory, open the SCHEMA.XML file.

2. In SCHEMA.XML you will see the top-level LIST element. This element defines the schema and views for the new list.

3. Replace the List start tag with the listed code below:

 		<List xmlns:ows="Microsoft SharePoint" Name="doclinks" 
 			Title="Document Link List" Direction="0" 
 			Url="Lists/DOCLINKSLIST" BaseType="0" >
The List element contains two child elements. The MetaData element defines the fields in the list and the Data element is used if you want a list to contain data when it is first created. The MetaData element contains the child elements DefaultDescription, Fields, Views, Toolbar and Forms.

The DefaultDescription is used on the Documents and Lists page when a list is created. This field is mandatory if you want to automatically create the list at site creation. If you fail to do this, you will get the generic “Action can not be completed” error at site creation.

<MetaData>
	<DefaultDescription>Document link repository</DefaultDescription>	
Next you need to define the list columns in the Fields element. The Fields element contains a set of Field elements that define the schema for the different list columns. If you want to add an additional field to a list, you can do this by inserting a new Field element as child of the Fields element. For our solution, we are going to add 2 extra columns. The following code should be added to:

<Fields>
<Field Name="MainDocURL" Type="Text" DisplayName="Main document URL" 
  Required="TRUE" Sealed="TRUE"/>
<Field Name="LinkedDocURL" Type="Text" DisplayName="Linked document URL" 
  Required="TRUE" Sealed="TRUE" />
In order to display the list as an option on the Create page in SharePoint, you need to specify the new list within the ListTemplates element of the ONET.XML file of your custom site definition. The ListTemplates element contains a set of ListTemplate elements. Each ListTemplate element is a reference to a list definition in the Lists directory of the custom site definition.

1. Add a new LISTTEMPLATE element. The ListTemplate element contains a number of attributes:

  • Type: Unique identifier for the list

  • BaseType: Specifies the base list type as defined in the BaseTypes section of ONET.XML, 0-Custom List

  • OnQuickLaunch: True if lists created from this definition will appear in the quick launch menu by default

  • SecurityBits: Currently not used in WSS

  • Hidden: True if the list will not appear on the Create page. For debugging purposes we will set this attribute to False

2. Copy the code listed below as first element behind the <ListTemplates> tag.

<ListTemplate Name="doclinkslist" DisplayName="Document Link List" Type="777" 
  BaseType="0" OnQuickLaunch="TRUE" SecurityBits="11" 
  Description="Custom list containing links between documents" 
  Image="/_layouts/images/itgen.gif" Hidden=”False”>
</ListTemplate>
3. Save ONET.XML

4. Perform an iisreset

To actually create the list whenever a new site is created based on the custom site definition, you will need to add some extra changes to ONET.XML. Which lists, web parts and web part pages are used in a site is defined in a Configuration within ONET.XML. Add your custom list to the <Configuration>. The <Lists> element contains the collection of lists for a specific configuration. The Configuration ID refers to our WEBTEMPSTSDOCLINKS.XML file.

 	<Configuration ID="0" Name="Default">
 	 	<Lists>
 		 <List Title="Document links" Type="999" />
 		  …
 		</Lists>
 	</Configuration>


Step 3: Extend the SharePoint document library context menu

For each item in a Windows SharePoint Services (WSS) document library a context sensitive drop-down menu is shown. For this application we are going to add an option to enable document linking.



The context menu for list items is generated by client-side Javascript contained in a file OWS.JS, which you can find in the folder \Program Files\Common Files\Microsoft Shared\web server extensions\ 60\TEMPLATE\LAYOUTS\1033. It is however possible to override functions in this file and to modify it according to your needs. But because OWS.JS is used by all sites in your SharePoint environment, it is not a good idea to modify OWS.JS directly. Instead you should make sure that changes only apply to your own custom site definition. To accomplish this, SharePoint provides a mechanism to override the functions in the OWS.JS file through the use of the CustomJSUrl attribute in ONET.XML.

The Project element in the ONET.XML file has an attribute CustomJSUrl in which you can provide your own javascript file that will be inserted into the pages of your template after the default OWS.JS.

1. Navigate to the following directory on your SharePoint server: \Program Files\Common Files\Microsoft Shared\web server extensions\ 60\TEMPLATE\LAYOUTS\1033

2. Make a copy of the ows.js javascript file and paste it into the same directory

3. Rename the copy to ows_custom.js

4. Navigate to the following directory on your SharePoint server in the custom site definition folder you just created: Drive:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\1033\STSDOCLINKS\XML and open ONET.XML for editing

5. Modify the Project element

<Project Title="Team Web Site" ListDir="Lists" xmlns:ows="Microsoft SharePoint" 
 CustomJSUrl="/_layouts/[%=System.Threading.Thread.CurrentThread.
CurrentUICulture.LCID%]/ows_custom.js">
6. Perform an iisreset

The actual menu is generated by the the AddDocLibMenuItems function. This function is already implemented in such a way that you can plug in your own code.

function AddDocLibMenuItems(m, ctx) {
  if (typeof(Custom_AddDocLibMenuItems) != “undefined”) {
    if (Custom_AddDocLibMenuItems(m, ctx)) {
      return;
    }
 }
}
By implementing Custom_AddDocLibMenuItems in a custom javascript file you can easily add your own functionality to the context menu. Open OWS_custom.js and insert the code below to add an two extra menuitem to the contextmenu.

function Custom_AddDocLibMenuItems(m, ctx)
{
 	var strDisplayText = "";
 	var strAction; 
 	var strImagePath = "";
 
 	// parse the URL out of the itemTable
 	var URL = "";
 	var index = itemTable.innerHTML.indexOf("href=");
 	if (index > 0)
 	{
 		var str = itemTable.innerHTML.substr(index + 6);
  		index = str.indexOf('"');
  		

 	 	if (index > 0)
  		{
   			URL = str.substr(0, index);
  		}
 	}
 
 	if (URL != "")
 	{
		
		var currentItemEscapedFileUrl = 
			escapeProperly(unescapeProperly(URL));  		



		linkToUrl = ctx.HttpRoot +  
"/_layouts/DOLMEN/linkto.aspx?docUrl=" + currentItemEscapedFileUrl;		

		strDisplayText = "Add links";
		strAction = "OpenLinkToPage('" + linkToUrl + "')";
		strImagePath = ctx.imagesPath + "itportlst.gif";

 	 	CAMOpt(m, strDisplayText, strAction, strImagePath);

 		linkToUrl = ctx.HttpRoot +  
"/_layouts/DOLMEN/managelinks.aspx?docUrl=" + currentItemEscapedFileUrl; 	
		strDisplayText= "View/Remove links";
		strAction = "OpenLinkToPage('" + linkToUrl + "')";
		strImagePath = ctx.imagesPath + "itdlsm.gif";
		
		CAMOpt(m, strDisplayText, strAction, strImagePath);
		
		CAMSep(m); 
	}
 
 	return false; 
}

function OpenLinkToPage(linkToUrl)
{
  var linkToPage = window.open(linkToUrl, "linkToUrl", 
	"width=700,height=430,nomenubar,noscrollbars,notoolbar");
  linkToPage.focus();
}
The CAMSep and CAMOpt – shown in the codelisting above - are both SharePoint specific functions which are defined in menu.js. The CAMOpt function is called to create individual menu items. The CAMOpt function takes four parameters: the menu object to add the new item to, the display text of the menu item, the javascript action to perform when the item is clicked and a path to an image file to associate with the item. The image for the item is stored in the directory Drive:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\IMAGES and is accessible through the imagesPath property of the context object. A call to the CAMSep function adds the separator bar to the menu. The “return false;” end statement is needed to make sure the standard menu items are also added to the menu; returning true indicates that these items should not be added.




Step 4: Add your custom linking page

In the previous step we modified the contextmenu to include a link to our own ASP.NET pages. These pages will be deployed into the _layouts virtual directory. All of SharePoints application pages are also deployed in the the /_layout/ virtual directory, e.g. the SharePoint pages to manage users, create lists, etc…

The ASP.NET handler in Windows SharePoint Services acts as a filter that determines the ASP.NET mode to use when running a page, which can be either direct or safe mode. Pages in the _layouts directory run in direct mode and are processed with the standard ASP.NET runtime processing model. Other pages which live inside SharePoint are executed in safe mode which means that no server-side code will be execution and only controls registered as safe will be rendered. All webpart pages within a SharePoint site are typically run in safe mode and are build dynamically by the SharePoint handler based on information in the SharePoint content databases.

The _layouts virtual directory maps to the physical folder \Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\LAYOUTS. So here you can add your own custom ASP.NET 2.0 pages.

To demonstrate how to add your own custom ASP.NET 2.0 pages in the _layouts directory we are first going to add a simple page which will display the title of the underlying SharePoint web.

1. Create a new folder underneath \Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE\LAYOUTS

2. Open Visual Studio 2005 and Select New WebSite > Empty website

3. Use as Location – File System and point it to the directory you created in step 1

4. Rightclick your project and add a webform – default.aspx

5. The custom contextmenu created in step 3, contains a link to linkto.aspx and managelinks.aspx so add these webforms to your project.

6. Rightclick your project and add a reference to the Windows SharePoint Services assembly. This will automatically add a web.config to your project since ASP.NET uses a unified storage location for all compilation and build settings (release/debug, assembly references, namespace imports, warning levelts, etc…) within the <compilation> section of the web.config.

7. Remove <authentication mode="Windows"/> from the web.config

8. Add a label to the webform

9. In order to perform actions on data within a SharePoint site, you must first obtain an SPWeb object, which serves as the starting point for accessing lists, items, documents, users, alerts, etc. You can obtain the SPWeb object by passing the current System.Web.HttpContext object to the GetContextWeb method of the SPControl class

10. Modify the code so that it looks like the code listed below

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;


public partial class _Default: System.Web.UI.Page
{
  protected void Page_Load(object sender, EventArgs e)
  {
    SPWeb myweb = SPControl.GetContextWeb(this.Context);
    this.lblTitle.Text = myweb.Title;
  }
}
11. Open the page in your webbrowser – it should display the title of the underlying SharePoint site in your label.

Tip: The easiest way to start debugging your code from within the context of SharePoint is just adding System.Diagnostics.Debugger.Break() within your code. This will signal a breakpoint to an attached debugger or ask which debugger to attach.

Both the linkto.aspx and managelinks.aspx page will have a similar look & feel. To keep things simple we are only going to allow linking between documents in the same document library. It is however not difficult to enhanced the described functionality and to include across document libraries and even across sites.

On the the linkto.aspx webform we are going to display a GridView with all documents in the same documentlibrary to which we can link. The GridView is an enhanced version of the basic DataGrid, in combination with new data source controls, you can bind to data without writing any code.

In our solution, we don’t want a document to be linked to itself, so we need to further filter out the items in the list. This can be accomplished using a SPQuery object. After instantiating an SPQuery object, you can use Collaborative Application Markup Language (CAML) to define criteria for the query, which is passed as parameter in the GetItems method. (For more info: see http://msdn.microsoft.com/library/en-us/spptsdk/html/tscamlovIntroduction_SV01029856.asp ) The GetItems method of the SPList class returns a collection of items from the list based on the specified query.

private SPListItemCollection _splistitems;
  private SPWeb _spweb;
  private string sSourceUrl;
  private string sSourceName;
  private string sFolderUrl;
  private SPList _splist;
  private Guid guidDocLib;

  protected void Page_Load(object sender, EventArgs e)
  {
    
    _spweb = SPControl.GetContextWeb(this.Context);
    
        
    sSourceUrl = Request.QueryString["docUrl"].ToString();
    sSourceUrl = SourceUrl.Substring(sSourceUrl.IndexOf(_spweb.ServerRelativeUrl));
    SPFile _spfile = _spweb.GetFile(sSourceUrl);
    sSourceName = _spfile.Name;
    sFolderUrl = _spweb.Name + "/" + _spfile.ParentFolder;
    guidDocLib = _spfile.ParentFolder.ContainingDocumentLibrary;
         
        
    if (!Page.IsPostBack)
    {
      DataTable dtDocLinks = this.GetDocumentData();
      this.GridView1.DataSource = dtDocLinks;
      this.GridView1.DataBind();           
    }
  }



  protected DataTable GetDocumentData()
  {
    DataTable dtDocLinks=null;

    _splist = _spweb.Lists[guidDocLib];
    
    SPQuery _spquery = new SPQuery();
    
    _spquery.Query = "<Where><Neq><FieldRef Name='FileLeafRef' />" + 
        "<Value Type='Text'>" + sSourceName + "</Value></Neq></Where>";
    
    _splistitems = _splist.GetItems(_spquery);
    
    dtDocLinks = _splistitems.GetDataTable(); 
        
    return dtDocLinks;
  }
In the gridview on the linkto.aspx webform, we are going to display 4 columns:

  • Document type icon

  • Title of the document with a hyperlink to the location of the document

  • Last modified date

  • Modified by – including presence icon

  • A column with checkboxes to select the documents which you want to link to the selected document

To give the external pages the same look of and feel as SharePoint, we are also going to add a reference to the WSS stylesheet. By default, the styles directory is at C:\Program Files\Common Files\Microsoft Shared\Web server extensions\60\TEMPLATE\LAYOUTS\1033\styles. 1033 is a reference to the language of your SharePoint installation (1033: LCID for english). Add a <LINK> tag within the <HEAD> which uses this stylesheet. For the gridview control we are going to apply the following styles:

  • <HeaderStyle CssClass="ms-vh2" />

  • <RowStyle CssClass="ms-vb2" />

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="linkto.aspx.cs" 
    Inherits="linkto" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
  <title>Document link management</title>
  <Link REL="stylesheet" Type="text/css" HREF="/_layouts/1033/styles/ows.css">
  <link rel="stylesheet" type="text/css" href="/_layouts/1033/styles/Menu.css"> 
  <script src="/_layouts/1033/owsbrows.js"></script>
  <script src="/_layouts/1033/ows.js"></script>
  <script src="/_layouts/1033/menu.js"></script>
  <script src="/_layouts/1033/ie55up.js"></script>
</head>
<body>
  <form id="form1" runat="server">
    <table cellpadding="2" cellspacing="3" border="0">
    <tr><td>&nbsp;</td></tr>
    <tr><td><table class="ms-toolbar" width="100%"><tr><td style="height:16px">
        <asp:Label ID="lblTitle" runat="server">
            Document link management - add links
        </asp:Label>
    </td></tr></table></td></tr>
    <tr><td>
    <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" 
        CellPadding="3">
      <Columns>
        <asp:TemplateField><ItemTemplate>
           <asp:Image ID="Image1" runat="server" 
            ImageUrl='<%# GetIconURL(DataBinder.Eval (
                Container.DataItem, "FileLeafRef").ToString()) %>' />
        </ItemTemplate></asp:TemplateField>
        <asp:TemplateField HeaderText="Title"><ItemTemplate>
          <a href="<%# GetFullUrl(DataBinder.Eval( 
                Container.DataItem, "FileLeafRef").ToString()) %>" 
            target="_blank">
            <%# DataBinder.Eval( Container.DataItem, "Title") %></a>
          <asp:Label ID="lblLinkedDocUrl" Visible="false" runat="server" 
            Text='<%# DataBinder.Eval( Container.DataItem, "FileLeafRef") %>'>
          </asp:Label>
        </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Modified" ItemStyle-Wrap=false>
        <ItemTemplate>
         <%# DataBinder.Eval( Container.DataItem,"Last_x0020_Modified") %>
        </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Modified By">
        <ItemTemplate>
        <%# GetPresenceString(
            DataBinder.Eval( Container.DataItem,"Editor").ToString())%>
        </ItemTemplate>
        </asp:TemplateField>
        <asp:TemplateField HeaderText="Select">
          <ItemTemplate>
            <asp:CheckBox ID="chkSelect" runat="server" />
          </ItemTemplate>
        </asp:TemplateField>
       </Columns>
      <HeaderStyle CssClass="ms-vh2" />
      <RowStyle CssClass="ms-vb2" />
    <EmptyDataRowStyle />
    </asp:GridView>
    </td></tr>
    <tr>
    <td align=right>
    <div style="padding-right:5px"><asp:Button ID="butOK" runat="server" 
        Text="OK" OnClick="butOK_Click" /></div>
    </td>
    </tr> 
  </table>
  </form>
</body>
</html>


The presence icon as shown in SharePoint pages is generated by a javascript function IMNRC, which in turn calls an ActiveX control (called "name.dll") on the users machine. To use this presence icon in your custom pages only requires a reference to the necessary javascript files and some client-side javascript.

protected string GetPresenceString(string sLoginName) 
  {
    string s="";
    SPUser _spuser = _spweb.AllUsers[sLoginName];
    s = "<span><img border=\"0\" height=\"12\" width=\"12\" 
        src=\"/_layouts/images/blank.gif\" onload=\"IMNRC('" + _spuser.Email + 
        "')\" id=\"IMID1\" ShowOfflinePawn=1>" + _spuser.Name + "</span>";

    return s;
  }
Adding links to the Document Link list from within our custom page is pretty straightforward. An important thing to remember however is that you should explicitly call the Update method after making changes to a SPListItem. This call will update the database with the changes you made. Below is the code listing for updating the Document Link list with the checked items in the gridview.

protected void butOK_Click(object sender, EventArgs e) {
    SPList _splistdoclinks = _spweb.Lists["Document Links"];
       

    for (int i = 0; i < GridView1.Rows.Count; i++) {
      GridViewRow row = GridView1.Rows[i];
      bool isChecked = ((CheckBox)row.FindControl("chkSelect")).Checked;
      if (isChecked) {
        string s = ((Label)row.FindControl("lblLinkedDocUrl")).Text;
        s = "/" + sFolderUrl + "/" + s;
        //Add listitems
        _spweb.AllowUnsafeUpdates = true;
        SPListItem _splistitem = _splistdoclinks.Items.Add();
        _splistitem["MainDocURL"] = sSourceUrl;
        _splistitem["LinkedDocURL"] = s;
        SPFile _spfile = _spweb.GetFile(s);
        _splistitem["Title"] = _spfile.Title;
        _splistitem.Update();
        _spweb.AllowUnsafeUpdates = false; 
      }
    }
    string popupScript="<script language='javascript'>window.close();</script>";

    ClientScript.RegisterStartupScript(typeof(Page),"PopupScript", popupScript);
}
Because I didn’t add a <SharePoint:FormDigest> control to my custom page, I need to set the AllowUnsafeUpdates property of the SPWeb class to true, to allow updates to the database.

In the second page “managelinks.aspx” we need to provide for a way to delete documentlinks. To avoid accidental deletions, the user will be shown a confirm messagebox upon clicking the delete link. This is easy to do because the ASP.NET 2.0 LinkButton has a an OnClientClick property in which you can specify client-side JavaScript that should be executed when the Button is clicked.

<asp:TemplateField>
  <ItemTemplate>
    <asp:LinkButton ID="lnkDelete" runat="server" 
    OnClientClick="return confirm('Are you sure you want to delete this link?')"
    CommandName="Delete" Text="Delete"></asp:LinkButton>
  </ItemTemplate>
 </asp:TemplateField>
A GridView can include OnRowDeleting and OnRowDeleted event handlers to trap and program RowDeleting and RowDeleted events. In the RowDeleting event handler we actually delete the document link. RowDeleting is a new event in .NET 2.0, it is raised when a row's Delete button is clicked, but before the GridView control deletes the row. This also enables you to still cancel the event.

protected void GridView1_RowDeleting(object sender, GridViewDeleteEventArgs e) {
    _splist = _spweb.Lists["Document Links"];

    GridViewRow row = GridView1.Rows[e.RowIndex];
    string s = ((Label)row.FindControl("lblLinkedDocUrl")).Text;
    
    SPListItemCollection _splistitems = _splist.Items;
    for (int j = 0; j < _splistitems.Count; j++) {
      if (
        (_splistitems[j]["MainDocURL"].ToString() == sSourceUrl) && 
        (_splistitems[j]["LinkedDocURL"].ToString() == s))
      {
        _spweb.AllowUnsafeUpdates = true;
        _splistitems.Delete(j);
        _spweb.AllowUnsafeUpdates = false;
      }
    }
    this.BindData();
}



Conclusion

With the release of Windows SharePoint Services SP2 it is finally possible to do SharePoint development in Visual Studio 2005, there are however some things you should take care off. In this article I have shown a sample solution which uses different SharePoint components such as custom site and list definitions, javascript files and ASP.NET 2.0 webforms in the _layouts directory. This article also demonstrates that SharePoint can be quite easily used as a development platform in which you can leverage the built-in functionality.


References


About the author

Joris Poelmans works at Dolmen, a Belgian IT services company and Microsoft Gold Partner. His main competence area is Information Worker solutions using both Windows SharePoint Services and SharePoint Portal Server, MS CMS and Office. He is a MVP for Windows SharePoint Services and one of the founding members of BIWUG - Belux Information Worker User Group - http://www.biwug.be. He regulary posts some SharePoint stuff on his blog at http://jopx.blogspot.com.
Joris Poelmans