Showing posts with label SharePoint Development. Show all posts
Showing posts with label SharePoint Development. Show all posts
17

Resolve SharePoint Errors caused by SQL Express

Today I tested my bulk import tool as part of a data migration. The code in the custom developed tool is straight-forward and must move list items and documents from over 600 SharePoint 2007 sites into a single SharePoint 2010 site.

Soon after starting my tests I ran into SharePoint foundation problems.

Problem:

Whenever the code attempts to upload a file into SharePoint I got the following error:

The URL 'Document_2011/0000000zzz/Test Document.docx' is invalid.

It may refer to a nonexistent file or folder, or refer to a valid file or folder that is not in the current Web. Troubleshoot issues with Microsoft SharePoint Foundation.

image

I then tried to manually delete a file from the SharePoint site and received the following error:

The server has encountered the following error(s):

ExampleFile.pdf

Exception from HRESULT: 0x80131904

When I try to delete the entire document library I receive the following error:

Exception from HRESULT: 0x80131904

Troubleshoot issues with Microsoft SharePoint Foundation.

Investigation:

I investigated the SharePoint content database.

In Central Admin, go to Application Management and then to Manage Content Databases.

image

You can select the web application and view the details of the associated content database.

image

I then logged into SQL Server Management Studio and confirmed that the content database ran out of space. As you can see from the screenshot below, the database size is 4138.19 MB and there is only 0.13 MB free space. (ensure to log into the correct instance of SQL in order to see the content databases)

image

Cause:

When SharePoint 2010 is installed the user can select to perform a ‘Basic’ or default installation.

Selecting a ‘Basic’ installation is a big mistake, but unfortunately guys who are new to SharePoint might not be aware of the pitfalls.

When you installed SharePoint 2007 as basic, the installation used the Windows Internal Database, a version of SQL Express with no size limit, but in SharePoint 2010 the installation will be done on SQL Express which has a 4GB size limit.

SQL Express 2008 R2 has a 10GB size limit, but it might still not be enough.

This was exactly my problem –SharePoint was installed as a ‘Basic’ installation on a test environment and I was trying to upload a large number of files – around 5.3GB in total size. Once I reached the limit of 4GB SharePoint could not perform any operations on the database which requires more space.

Remember that even if you try to delete items from SharePoint, they are only moved to the recycle-bin so the used database space does not become available immediately.

Solution:

There are two ways to solve this:

1-You can either upgrade from SQL Express to SQL Server,

or

2- Implement EBS (External Blob Store - move the BLOB items out of the database onto a file system. For more information on this please see http://msdn.microsoft.com/en-us/library/bb802812.aspx )

I decided to go with option #1-Upgrade my SQL Instance from SQL Express to SQL Server 2008 R2.  The better architecture would be to go with EBS but I wanted to explore the database upgrade and do the EBS later.

Upgrade Steps:

1-Close SharePoint and Close SQL Server Management Studio.

2-Open Command Prompt and run the following command from the location where the SQL Server installation files are available to start the installation (upgrade) of SQL Server R2.

Setup.exe SKUUPGRADE=1

image

3-Proceed through the installation wizard and on the Installation window click on ‘Upgrade from SQL Server 2000, SQL Server 2005 or SQL Server 2008. 

image

4-Proceed through the wizard until you get to the ‘Select Instance’ page.

Remember when we inspected our database details in Central Admin we were able to see the SQL instance:

image

5-Now, in the SQL Upgrade wizard you have to select the same instance. You can see from the screenshot below that my “SHAREPOINT” instance was originally installed as ‘Express’ so this is the one which I have to select to be upgraded to SQL Server 2008 R2. 

image

6-Click on Next and continue through the wizard….When you get to the “Instance Configuration” page pay careful attention to ensure that all the details are correct.

image

8-Click on Next and proceed through all the pages to complete the all the installation wizard steps.

9-Once completed review the results to confirm that the upgrade was successful.

 image

10-After this perform an IISReset.

Verify Success:

I viewed the properties of my upgraded database and immediately noticed that the space available was increased from 0.13MB to 1.02MB.

image

Also, after this I was able to add more content to SharePoint and grow the database size beyond 4GB..

image

Enjoy!!

Thanks to Todd Klindt - http://www.toddklindt.com/blog/Lists/Posts/Post.aspx?ID=55 for his contributions.

5

SharePoint 2007 Site Templates vs. Site Definitions


There is an ongoing debate between SharePoint developers regarding the best approach for packaging and deploying customized SharePoint solutions.
One easy approach is to use site templates, but my opinion is that for a developer who are concerned about system performance and who needs finer control over cusomization, a site definition is the better option.

I found that building some sort of custom site provisioning provider works the best.

Below is my analysis of the differences between site definitions vs. site templates and at the bottom is a summary of the approach I prefer for deploying custom SharePoint solutions.

What is a Site Definition?
A Site definition is the core definition of what a site is in SharePoint.
User content on a SharePoint site is located in a SQL Server database.
All the code and the pages that are used to administer and render the SharePoint site are stored as physical files on the web server.
A site definition exists as physical files on the web server and defines, through the use of SharePoint’s XML schema (known as CAML), the lists, columns, features etc. that should be created / activated when a site is created. (The files includes .aspx pages and .xml files)
A site definition is installed on file system of web front ends, located at ..\12\Template\SiteTemplates. This directory is language-neutral.
Site definition files are cached in memory on the server at process start-up of Microsoft Internet Information Services (IIS), which improves scalability and performance by reducing unnecessary data storage or retrieval, and by allowing uncustomized pages to be reused across sites. The information contained in these files is pulled from the cache at run time. Pages and list schemas are read from the site definition files but appear to be actual files within a site.
 
What is a Site Template?
A site template (*.stp file) is created through the user interface or through implementation of the object model. It is a package containing a set of differences and changes from a base site definition.
The site template package is stored as a CAB-based file that can be downloaded or uploaded to site collections by users with the appropriate rights.
It is not as performant as a pure site definition as site definitions are cached on start of IIS on the web front ends while site templates are stored and hence need to be fetched from the content database and merged with the site definition code at runtime to render the site.


Deciding Between Site Definitions and Custom Site Templates
 
Site Templates

  • Easy to create.
  • You do not need to be a server administrator on the Web server to create and deploy site templates.
  • Easiest way for system administrators to extend SharePoint without requirement for development skills.
  • Deployment is simple because template data is stored centrally in the configuration database.
  • Because it is slow to store templates in and retrieve them from the database, site templates can result in slower performance.
  • The contents of unghosted pages are routed through the Safe Mode parser, which prevents server-side code from executing, and which depends entirely on the Safe Controls list specified in the web.config file of the wwwroot directory to determine which controls can be rendered at run time.
  • You can modify a site template without affecting existing sites created by the template.
  • Custom templates can be modified without affecting existing sites that have been created from the templates.
  • Custom templates are not created in a development environment.
  • If the site definition on which the custom template is based does not exist on the front-end server or servers, the custom template will not work.
  • If you plan to transfer a site template to separate farm, that the farms have the same versions installed of SharePoint installed. (hotfixes,etc.) This is due to the dependence site templates have on the original base site definition they were created from.
Site Definitions:
  • Complex and laborious to create.
  • Administrators must always be involved in the deployment of site definitions.
  • Most appropriate for third-party developers.
  • Deploying a site definition requires more work.
  • Site definition files are cached in memory on the server at process start-up of Microsoft Internet Information Services (IIS), which improves scalability and performance by reducing unnecessary data storage or retrieval.
  • Uncustomized pages to be reused across sites.
  • You can achieve a finer level of customization by directly editing all the schema files.
  • Custom site definitions are version and upgrade independent.
  • Subsequent upgrades to SharePoint Products and Technologies may overwrite existing default site definitions. Using custom site definitions excludes your sites from potential upgrade issues.
  • There is no easy way to modify site definitions once they are deployed
  • Doing anything other than adding code can break existing sites
  • Users cannot apply a SharePoint theme through a site definition.
  • Customizing site definitions requires access to the file system of the front-end Web server.
  • Multiple root-level pages -- In a site definition, you can define several root-level pages. Instead of your sites having one page, they can have many.
  • Template picker pages -- You have the ability to change the icon that displays in conjunction with the template so it stands out as you create the templates.
  • Microsoft doesn't support changes made to a site definition after you've created the sites.
  • Multiple lists of the same type -- Because of the structure of CAML and the ONET.XML file, you can't create multiple lists from the same list definition and put them on the same page.
Summary of my preferred approach:
I found that building a custom site provisioning provider works the best.
This means that I build my SharePoint artefacts - lists, libraries, web parts, custom forms, etc. using SharePoint, SharePoint Designer and Visual Studio. When I am then ready to package and deploy my custom SharePoint solution I use the SharePoint Solutions Generator - part of Microsoft SharePoint Developer Tools 9.0 to create list and library definition features.
Once I have my list and library definitions in place I create a Visual Studio WSP Builder project with an Feature Event Receiver.
In the "activate" event I add logic to activate the definition features and in the "deactivating" event I do a cleanup (deactivate definition features and remove web parts)


This means the following will happen when one of my solutions are deployed / retracted:
1. The user creates a blank SharePoint site. (previously team site)
2. The user runs a Setup.exe file.
3. This file is the un-customized Codeplex Solutions Installer and will read configuration data from Setup.exe.config
4. The file will open a deployment wizard which guides the user through a series of screens to ensure successful solution deployment.
5. After successful deployment the user follow instructions from the deployment guide to execute a single stsadm.exe command.
6. This command will activate a single hidden feature.
7. The single feature which gets activated will do the following on the site:
  • Set the new site title.
  • Check environment for previously installed features which are still active
  • Activate Web Features in the correct order 
  • Activate Site Collection Features
  • Allows activation of generic site collection features – shared among more than 1 solution.
  • Clear Site Structure – remove unwanted artefacts
  • Set Site Structure – build Quick Launch Items, Set Web Parts on Default.aspx page etc.
8. During deactivation of the single “configuration” feature the following will happen:
  • Set the new site title back to default.
  • Deactivate Web Features in the correct order 
  • Deactivate Site Collection Features
  • Clear Site Structure – remove unwanted artefacts
  • I do not delete lists or libraries which contain data – do not want to delete data.
Benefits of this approach:
  • Allows dependencies between hidden features - lookup lists
  • Allows list to be automatically instantiated with default data
  • Allows library content – like UDCX files to be configured with new environmental values (link InfoPath Form to dynamic data connections)
  • Allows activation of web part features to site collection scope
  • Allows validation and upload of InfoPath Forms (using site collection feature)
  • Allows publishing of InfoPath Form to site collection forms library
  • Leverage off the benefits of using definitions. 

I hope this will help you to choose the best approach for the kind of SharePoint solutions which you need to deploy... good luck !!


Further reading and references:
http://itfootprint.wordpress.com/2007/04/18/sharepoint-site-template-vs-site-definition/
http://msdn.microsoft.com/en-us/library/ms980852.aspx
http://mymemorysucks.wordpress.com/2009/02/25/site-definitions-vs-site-templates-and-site-provisioning-providers/
http://my.advisor.com/doc/17614
http://blogs.msdn.com/b/brianwilson/archive/2008/07/13/site-definitions-versus-site-templates-and-deciding-on-the-correct-customization-approach.aspx

14

Custom settings for a SharePoint list or library

Someone asked me this week if it is possible to store custom settings (configuration) on a SharePoint (WSS 3.0 and MOSS 2007) list or library.
Example: You have a list or library and you want to create a custom property (metadata)field and provide the user with an GUI to manage a setting value.

I looked into the various options and found it is indeed very simple to accomplish.
 Many thanks to Margriet Bruggeman and Nikander Bruggeman for their contributions.
You can use the Rootfolder object which contains a System.Collections.Hashtable for storing and accessing properties.It is very simple.

To store the value you use:
SPContext.Current.List.RootFolder.Properties[strPropertyKey] = strValue;

SPContext.Current.List.RootFolder.Update ();
 
To retrieve the value you use:
strValue = SPContext.Current.List.RootFolder.Properties[strPropertyKey].ToString();

In order to make it easy for the user to manage such settings, one would add a custom action with Location="Microsoft.SharePoint.ListEdit".
This means that when the user go to a list or library and then select "Settings" a new hyperlink (shortcut) will exist on the settings page from where the custom settings can be managed. (this is not overriding the settings page but rather just adding a shortcut on it)

This tutorial will show how easy it is to develop the described functionality.

Overview:
We will develop the following 2 components:
  1-Feature with Custom Action.
  2-Custom aspx page with code behind to view,insert,edit & delete settings.

Lets get started:

Create a new VS2008 WSPBuilder Project.

















Add a new item to the project. Select WSPBuilder and then 'Blank Feature'















Set the scope of the feature to web.

The Feature.xml file will look like:
<?xml version="1.0" encoding="utf-8"?>

<Feature Id="65a738a0-a095-489e-be19-82c077edea9f"
Title="ListCustomSettings"
Description="Allow Custom Settings on any list"
Version="12.0.0.0"
Hidden="FALSE"
Scope="Web"
DefaultResourceFile="core"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementManifest Location="elements.xml"/>
</ElementManifests>
</Feature>

Now we will use the feature to implement a custom action.
Change the Elements.xml file of the Feature to look like:
<?xml version="1.0" encoding="utf-8" ?>

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Id="Custom.Configuration.ListCustomSettings"
GroupId="GeneralSettings"
Location="Microsoft.SharePoint.ListEdit"
RequireSiteAdministrator="FALSE"
Sequence="100"
Title="Custom Settings">
<UrlAction
Url="_layouts/ListCustomSettings/ListSetting.aspx?List={ListId}" />
</CustomAction>
</Elements>

The feature (as above) will give us a new hyperlink on the List Settings Page (ListEdit). It will look as follow:












The next step is to create a custom page which we can redirect to and on which we can mange the custom settings.

Add a .aspx page to your project with an associated .aspx.cs file.
(there is a little trick to adding this aspx page to the project... please see the footnote at the bottom of this post for step-by-step instructions)

Ensure that the two files are placed inside an appropriate solution folder within the LAYOUTS Folder. Also ensure that the name of the aspx file corresponds to the url used in the custom action definition (above).
<UrlAction Url="_layouts/ListCustomSettings/ListSetting.aspx?List={ListId}" />



















Next we add controls, assembly references etc to our aspx page. I am not going into detail of each line of code as it is standard code.
Ensure your .aspx file look as follow:
(please take special note of the public token key as yours might be different)
<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Import Namespace="System.Data" %>
<%@ 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="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="wssuc" TagName="ToolBar" Src="~/_controltemplates/ToolBar.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ToolBarButton" Src="~/_controltemplates/ToolBarButton.ascx" %>
<%@ 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" %>
<%@ Page Language="C#" MasterPageFile="~/_layouts/application.master" AutoEventWireup="true" Inherits="ListConfig.ListConfig, ListCustomSettings,Version=1.0.0.0,Culture=neutral,PublicKeyToken=29d1ff49fe67cd5b" %>
<asp:Content ID="Content2" ContentPlaceHolderID="PlaceHolderPageTitle" runat="server">
Custom list config settings page
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server">
Custom list config settings
</asp:Content>
<asp:Content ID="Content1" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<table width="50%" class="ms-propertysheet" cellspacing="0" cellpadding="0" border="0">
<tr>
<td class="ms-error">
<asp:Label ID="lblMessage" runat="server" EnableViewState="False" />
</td>
</tr>
</table>
<table width="50%" class="ms-propertysheet" cellspacing="0" cellpadding="0" border="0">
<tr><td class="ms-descriptionText"><asp:Label ID="LabelMessage" Runat="server" EnableViewState="False" class="ms-descriptionText"/></td></tr>
<tr><td class="ms-error"><asp:Label ID="LabelErrorMessage" Runat="server" EnableViewState="False" /></td></tr>
<tr><td class="ms-descriptionText"><asp:ValidationSummary ID="ValSummary" HeaderText="Errors" DisplayMode="BulletList" ShowSummary="True" runat="server"></asp:ValidationSummary></td> </tr> <tr> <td><img src="/_layouts/images/blank.gif" width="10" height="1" alt="" /></td></tr>
</table>
<table width="50%" border="0" cellspacing="0" cellpadding="0" class="ms-propertysheet">
<!-- Key -->
<wssuc:InputFormSection Title="Key" runat="server">
<Template_Description>
<SharePoint:EncodedLiteral ID="EncodedLiteral1" runat="server" text="Specify a key for the entry." EncodeMethod='HtmlEncodeAllowSimpleTextFormatting'/>
</Template_Description>
<Template_InputFormControls>
<wssuc:InputFormControl LabelText="" runat="server">
<Template_control>
<wssawc:InputFormTextBox Title="Key" class="ms-input" ID="txtKey" Columns="35" Runat="server" MaxLength=255 />
</Template_control>
</wssuc:InputFormControl>
</Template_InputFormControls>
</wssuc:InputFormSection>
<!-- Value -->
<wssuc:InputFormSection Title="Value" runat="server">
<Template_Description>
<SharePoint:EncodedLiteral ID="EncodedLiteral2" runat="server" text="Specify the value of the entry" EncodeMethod='HtmlEncodeAllowSimpleTextFormatting'/>
</Template_Description>
<Template_InputFormControls>
<wssuc:InputFormControl LabelText="" runat="server">
<Template_control>
<wssawc:InputFormTextBox Title="Value" class="ms-input" ID="txtValue" Columns="35" Runat="server" MaxLength=255 />
</Template_control>
</wssuc:InputFormControl>
</Template_InputFormControls>
</wssuc:InputFormSection>
<wssuc:ButtonSection runat="server" ShowStandardCancelButton="false">
<template_buttons>
<asp:PlaceHolder ID="PlaceHolderInsertUpdate" runat="server">
<asp:Button UseSubmitBehavior="false" runat="server" class="ms-ButtonHeightWidth" Text="Insert" id="cmdInsertUpdate" OnClick="cmdInsertUpdate_OnClick"/>  
<asp:Button UseSubmitBehavior="false" runat="server" class="ms-ButtonHeightWidth" Text="Cancel" id="cmdInsertUpdateCancel" OnClick="cmdInsertUpdateCancel_OnClick"/>  
</asp:PlaceHolder>
</template_buttons>
</wssuc:ButtonSection>
<br />
<br />
</table>
<SharePoint:SPGridView ID="grdEntries" runat="server" AutoGenerateColumns="false" Width="50%" AllowSorting="True" OnRowDeleting="grdEntries_RowDeleting" OnRowEditing="grdEntries_RowEditing" EnableViewState="false">
<AlternatingRowStyle CssClass="ms-alternating" />
</SharePoint:SPGridView>
<wssuc:ButtonSection runat="server" ShowStandardCancelButton="false">
<template_buttons>
<asp:PlaceHolder ID="PlaceHolder1" runat="server">
<asp:Button UseSubmitBehavior="false" runat="server" class="ms-ButtonHeightWidth" OnClick="cmdOK_OnClick" Text="OK" id="cmdOK" causesvalidation=false />
</asp:PlaceHolder>
</template_buttons>
</wssuc:ButtonSection>
<br />
<br />
<SharePoint:FormDigest ID="FormDigest1" runat="server" />
</asp:Content>

Now we need to code the ListConfig class which is the code-behind of our aspx page.
Open the ListSettings.aspx.cs in code view and add the following code:
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;
using Microsoft.SharePoint.Utilities;
using System.Xml;

namespace ListConfig
{
  public partial class ListConfig : LayoutsPageBase
  {
    private DataTable _dtEntries;
    private DataView _dvEntriesView;
    protected SPGridView grdEntries = new SPGridView();
    protected Label lblMessage = new Label();
    protected InputFormTextBox txtKey = new InputFormTextBox();
    protected InputFormTextBox txtValue = new InputFormTextBox();
  }
}

 Next, add a Page_Init event to the ListConfig class. In this event will we add two data columns and two command cokumns to our grid.
protected void Page_Init(object sender, EventArgs e)

{
  BoundField objKeyField = new BoundField();
  objKeyField.ItemStyle.Width = Unit.Percentage(30);
  objKeyField.DataField = "Key";
  objKeyField.HeaderText = "Key";
  grdEntries.Columns.Add(objKeyField);

  BoundField objValueField = new BoundField();
  objValueField.DataField = "Value";
  objValueField.HeaderText = "Value";
  grdEntries.Columns.Add(objValueField);

  CommandField objEditField = new CommandField();
  objEditField.ShowEditButton = true;
  objEditField.HeaderText = "Edit";
  objEditField.ItemStyle.Width = Unit.Pixel(20);
  objEditField.ButtonType = ButtonType.Image;
  objEditField.EditImageUrl = "/_layouts/images/edit.gif";
  grdEntries.Columns.Add(objEditField);

  CommandField objDeleteField = new CommandField();
  objDeleteField.ShowDeleteButton = true;
  objDeleteField.HeaderText = "Delete";
  objDeleteField.ItemStyle.Width = Unit.Pixel(20);
  objDeleteField.ButtonType = ButtonType.Image;
  objDeleteField.DeleteImageUrl = "/_layouts/images/delete.gif";
  grdEntries.Columns.Add(objDeleteField);
}


Next, add a Page_Load event to the ListConfig class. In this event we set allowunsafeupdates=true and we bind the grid to data.
protected void Page_Load(object sender, EventArgs e)

{
  SPContext.Current.Web.AllowUnsafeUpdates = true;
  BindGrid();
} 

Next, add a LoadData function to help load the data into the datatable. You will notice the important thing here is that we iterate through the RootFolder.Properties.Keys to get all custom settings for the current list, and then we load the data in to the datatable.
private void LoadData(string strSortOrder)

{
_dtEntries = new DataTable();
DataColumn objKeyColumn = new DataColumn();
objKeyColumn.DataType = Type.GetType("System.String");
objKeyColumn.ColumnName = "Key";
objKeyColumn.ReadOnly = true;
_dtEntries.Columns.Add(objKeyColumn);

DataColumn objValueColumn = new DataColumn();
objValueColumn.DataType = Type.GetType("System.String");
objValueColumn.ColumnName = "Value";
objValueColumn.ReadOnly = true;
_dtEntries.Columns.Add(objValueColumn);

foreach (string strKey in SPContext.Current.List.RootFolder.Properties.Keys)
{
if (strKey.StartsWith("vti_")) continue;
if (strKey.StartsWith("_reporting")) continue;
if (SPContext.Current.List.RootFolder.Properties[strKey] == null) continue;
if (SPContext.Current.List.RootFolder.Properties[strKey].ToString() == String.Empty) continue;

DataRow objRow = _dtEntries.NewRow();
try
{
objRow["Key"] = strKey;
}
catch
{
objRow["Key"] = "Error";
}
try
{
objRow["Value"] = SPContext.Current.List.RootFolder.Properties[strKey];
}
catch
{
objRow["Value"] = "Error";
}
_dtEntries.Rows.Add(objRow);
}
_dvEntriesView = _dtEntries.DefaultView;
_dvEntriesView.Sort = "[Key] asc";
}

Next, add a helper function to populate the grid:
private void BindGrid()

{
try
{
LoadData(String.Empty);
grdEntries.DataSource = _dvEntriesView;
grdEntries.DataBind();
}
catch (Exception err)
{
lblMessage.Text = lblMessage.Text + "Failed to bind data to page. Please try to reload the page.";
}
}

Add Event handlers for the grid behaviour:
public void grdEntries_RowDeleting(object sender, GridViewDeleteEventArgs e)

{
try
{
string strKey = grdEntries.Rows[e.RowIndex].Cells[0].Text;
strKey = SPHttpUtility.HtmlDecode(strKey);
if (SPContext.Current.List.RootFolder.Properties.ContainsKey(strKey))
{
SPContext.Current.List.RootFolder.Properties[strKey] = null;
SPContext.Current.List.RootFolder.Update();
}
BindGrid();
}
catch (Exception err)
{
lblMessage.Text = lblMessage.Text + err.Message + "\n";
}
}

public void grdEntries_RowEditing(object sender, GridViewEditEventArgs e)
{
string strKey = grdEntries.Rows[e.NewEditIndex].Cells[0].Text;
txtKey.Text = strKey;
txtKey.Enabled = false;
txtValue.Text = SPContext.Current.List.RootFolder.Properties[strKey].ToString();
}

public void grdEntries_Sorting(object sender, GridViewEditEventArgs e)
{
grdEntries.DataSource = _dvEntriesView;
grdEntries.DataBind();
}

Add a helper function to build a redirect url (which will be used when the form close):
private string GetRedirectUrl()

{
return SPContext.Current.Web.Url + String.Format("/_layouts/listedit.aspx?List={0}", Request.QueryString["List"]);
}

Add the Button Click Events which corresponds to the definition in the aspx file:
public void cmdOK_OnClick(object sender, EventArgs e)

{
SPUtility.Redirect(GetRedirectUrl(), SPRedirectFlags.UseSource, HttpContext.Current);
}

public void cmdInsertUpdate_OnClick(object sender, EventArgs e)
{
try
{
string strKey = txtKey.Text;
if (strKey.Length == 0)
{
throw new Exception("You must provide a key.");
}
if (strKey.Contains("&"))
{
throw new Exception("& is not allowed. You must provide an alternative name.");
}
string strValue = txtValue.Text;
if (strValue.Length == 0)
{
throw new Exception("You must provide a value.");
}
if (SPContext.Current.List.RootFolder.Properties.ContainsKey(strKey))
{
SPContext.Current.List.RootFolder.Properties[strKey] = strValue;
}
else
{
SPContext.Current.List.RootFolder.Properties.Add(strKey, strValue);
}
SPContext.Current.List.RootFolder.Update();
strKey = "";
strValue = "";
txtKey.Text = "";
txtKey.Enabled = true;
txtValue.Text = "";
BindGrid();
}
catch (Exception err)
{
lblMessage.Text = err.Message;
}
}

public void cmdInsertUpdateCancel_OnClick(object sender, EventArgs e)
{
txtKey.Text = "";
txtKey.Enabled = true;
txtValue.Text = "";
}

Override the OnUload event of the page to set the AllowUnsafeUpdates=false:
protected override void OnUnload(EventArgs e)

{
SPContext.Current.Web.AllowUnsafeUpdates = false;
}

WOW, it looks like a lot of code, but in fact, its not much!
Next, build your project and if all is successfull you are ready to test the outcome.

Go to your project properties and set the 'start browser with' setting to the target SP site url on which you want to test.













Save your project. Right click on your project, select WSPBuilder and then select "Build WSP" - keep an eye on the output pane to ensure that there are no problems. Once successful, right click on the project, select WSPBuilder and then select "Deploy". 
Once done, go to the target SP site and refresh the site features list.
You will notice the following new site feature is available:






Activate the feature.
Go to any list or library and open the settings page.














You will notice the new Custom Action URL which we added:













When you click on the 'custom settings' custom action url you will be redirected to the new custom page:










On this page you can manage custom settings for the list.












There you go ! a very nice tool which can be used to store custom settings on any list or library.

Enjoy !!!

Note: Trick to add a aspx page to the project:
If you want to add a aspx page to the project you click on project and select "Add New Item..." but you will notice that the Web Form / .aspx item is not available to be selected.
When you want to add a aspx page with an associated aspx.cs file to the project there is a little trick that you need to do.
Open another instance of Visual Studio.
Select to create a new project and choose C# --> ASP.Net Web Application
















Provide a projace name and click on "OK".
Once the project has been created you will see it contains a web form (aspx).
Open the Solution Explorer view --> right click and select "Open Folder in Windows Explorer".






















From Windows Explorer copy the aspx file and aspx.cs file into your SharePoint project folder (where the solution of this tutorial project is located) and then in your project add an existing item and select the two files which you copied.

5

Silverlight in SharePoint - Option 2 - Custom Web Part Feature

In two previous tutorials I showed how to create a simple Silverlight application, and how to use a Silverlight application inside SharePoint (WSS 3.0 or MOSS 2007) using a Content Editor Web Part.

There are a lot of conflicting and even unclear instructions on the web on how to achieve this.
Thank you Karine Bosch for the excellent post you made available. It is very well structured and helped me immensely in putting together this post for my team. The original blog post which a lot of my information come from can be found at http://karinebosch.wordpress.com/2009/07/17/integrating-silverlight-3-in-sharepoint-2007/

In this tutorial I will show how to develop a proper custom SharePoint web part feature which encapsulates the Silverlight application.


Please note that the example I give is very basic. It will be easy to make the web part more generic - you can add web part properties in which which the user can specify the height, width, and .xap file location. If you add the custom properties you will have a reusable Silverlight container web part.

Important:
This tutorial is for Silverligh 3 - there has been changes from Silverlight version 2 which you need to consider. (SL3 does not contain the System.Web.Silverlight.dll)

Prerequisites:
       Silverlight 3 Tools for Visual Studio (download here)
       SharePoint (WSS 3.0 or MOSS 2007)
       Visual Studio 2008
       A working Silverlight application (see this tutorial to create one)
       WSP Builder (to develop the SharePoint Web Part)

Lets Create our SharePoint Web Part:
Create a new Visual Studio 2008 project.
Select "WSPBuilder" from project type and  "WSP Builder Project" from Templates.














Right-click on the project and add a new item. Select WSPBuilder from Categories and "Web Part Feature" from Templates.













Provide a description for the web part and set the Scope to "Site"












In the Solution Explorer, create a LAYOUTS folder under TEMPLATES and then create 2 new folders under the LAYOUTS folder. Name the 2 new folders "SL3" and "XAPS".
Inside the XAPS folder add an existing working Silverlight 3 application.
(see this tutorial to create one)




















Now we have a structure to build our web part code and we have a Silverlight application which we can display in our web part.
Next we will add two Silverlight helper files. (which enables Web sites to create advanced Silverlight installation and instantiation experiences)

In Windows Explorer locate the Silverlight.js file from the C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Tools folder.
Copy this file to the SL3 folder of your project.
(For more information on Silverligth.js please see this site.)

In Visual Studio Solution Explorer switch-on the "view all files" option, refresh your solution and then right click on Silverlight.js and select "Include in project". Alternatively use the "add to project" functionality.

Right Click on the SL3 folder and select "Add" --> "New Item"
Select Visual C# Items and Select JScript File.
Name the File SharePointSilverLightHelper.js













Once the file is added to your solution, open the file and add the following script to create a Silverlight application based on the silverlight.CreateObjectEx method:
function onSilverlightError(sender, args)
{
var appSource = "";
if (sender != null && sender != 0)
{
 appSource = sender.getHost().Source;
}
var errorType = args.ErrorType;
var iErrorCode = args.ErrorCode;
var errMsg = "Unhandled Error in Silverlight Application " + appSource
+ "\n" ;
errMsg += "Code: "+ iErrorCode + " \n";
errMsg += "Category: " + errorType + " \n";
errMsg += "Message: " + args.ErrorMessage + " \n";
if (errorType == "ParserError")
{
 errMsg += "File: " + args.xamlFile + " \n";
 errMsg += "Line: " + args.lineNumber + " \n";
 errMsg += "Position: " + args.charPosition + " \n";
}
else if (errorType == "RuntimeError")
{
 if (args.lineNumber != 0)
 {
  errMsg += "Line: " + args.lineNumber + " \n";
  errMsg += "Position: " + args.charPosition + " \n";
 }
 errMsg += "MethodName: " + args.methodName + " \n";
}
 throw new Error(errMsg);
}


function createSL(divid, swidth, sheight, source, initparameters)
{
  var pluginid = divid + "Plugin";
  var divElement = document.getElementById(divid);
  var altHTML = divElement.innerHTML;
  if (swidth == null)
   {swidth='100%';}
  if (sheight == null)
   {sheight='750px';
}
  Silverlight.createObjectEx(
 {  source: source,
    parentElement: divElement,
    id: pluginid,
    properties:
    {
     width:swidth, 
     height:sheight,
     minRuntimeVersion:'2.0.31005.0'
  },
   events:
   {
    OnError: onSilverlightError 
   },
   initParams: initparameters
   }
  );
}
 
The solution file structure should look like this:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Save the Visual Studio solution.
 
Next, we will add the web part code to render the Silverlight application inside SharePoint.

Open your web part class (in this example SharePointSilverLightWebPart1.cs) in code view and remove all the unneccesary code so that the class is nice and clean:
using System;
using System.Collections.Generic;

using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;
using System.Text;

namespace SharePointSilverLightWebPart1
{
   [Guid("848d4aec-5dd2-4b7d-a512-995191a627e8")]
   public class SharePointSilverlightWebPart1 :  
          Microsoft.SharePoint.WebPartPages.WebPart
   {
   }
}

Add the following declaration inside the class:
private LiteralControl silverlightHost;

Add code to the OnPreRender event to register the javascript files.
protected override void OnPreRender(EventArgs e)
{
  base.OnPreRender(e);
  ClientScriptManager cs = Page.ClientScript
  if (!cs.IsClientScriptIncludeRegistered("sl_javascript"))
     cs.RegisterClientScriptInclude(this.GetType(),
     "sl_javascript", "/_LAYOUTS/SL3/Silverlight.js");
  if (!cs.IsClientScriptIncludeRegistered("spsl_javascript"))
    cs.RegisterClientScriptInclude(this.GetType(), 
    "spsl_javascript", /_LAYOUTS/SL3/SharePointSilverLightHelper.js");
}

Call the above javascript from within the CreateChildControls method:
protected override void CreateChildControls()
{
  string xaplocation = null;
  xaplocation = "_LAYOUTS/Xaps/SilverlightApplication1.xap";
  Unit height = new Unit(500);
  string slstring = string.Format("<script 
  type=\"text/javascript\">createSL('silverlightHost', '{0}', 
  '{1}', '{2}', '{3}');</script>", height, height, xaplocation, 
  "");
  silverlightHost = new LiteralControl(string.Format("<div  
  id=\"silverlightHost\" style=\"width:100%; height:100%\"></div>
  {0}", slstring));
  this.Controls.Add(silverlightHost);
}

To test the solution, add a SharePoint site URL to the Debug settings of the project.


















Save the project. Build the project. Use WSP Builder to "Build WSP" and then to "Deploy"
Navigate to the same SharePoint site in I.E. and go to Site Settings -->Site Collection Features. Activate your new feature.














Edit a web part page on your site and add the following web part:










See the new web part containing the Silverlight solution.

Enjoy !!