Friday, December 11, 2009

Failed on Start (retrying) status in workflow

This is an error that appears in workflow status when the workflow fails to start.
We should not always blame the Windows Workflow Framework thinking that it could be due to it.
Atleast in my case, every time it happened due to program errors.
In our SharePoint environment, in most cases, it occured due to unmatched assembly name, public key token or version in the workflow.xml or whatever element file we included with the sharepoint feature.
Few times it occured due to wrong end points in the rules.xml file as well.

So if the assembly info is all correct, check to make sure that the WCF end points / class names are all correct if using WCF service(s) with Workflow

REF: http://blogs.msdn.com/wael/archive/2008/01/11/workflow-failed-on-start-retrying.aspx

Friday, November 20, 2009

This solution contains no resources scoped for a Web application and cannot be deployed to a particular Web application when you deploy a workflow

The below error appeared when I tried to deploy a workflow onto a specific web application:
This solution contains no resources scoped for a Web application and cannot be deployed to a particular Web application

After a bit of research, I have learnt that a solution with workflow cannot be deployed on a specific web application. It needs to be deployed globally.

Ex: stsadm -o deploysolution -name workflowsln -url http://localhost/ fails

If the solution'manifest.xml file specifies a <SafeControl> element, we need to specify the url parameter when invoking stsadm.exe as SharePoint needs to know which Web.config file to merge the <SafeControl> elements into, and therefore the url parameter must be specified.
The bottom line is that if your solution specifies elements (a.k.a. "resources") that need to be merged into a Web.config file (i.e. "for a Web application") then you must specify the url parameter. If your solution does not have an assembly or if your solution contains workflows, then you cannot specify the url parameter.

Reference: http://blogs.msdn.com/jjameson/archive/2007/06/17/issues-deploying-sharepoint-solution-packages.aspx

Wednesday, October 28, 2009

How to debug a Self-hosted WCF Service

After manually attaching the debugger to the hosting process to debug the code, I got this error when I try to debug a self-hosted WCF service:
System.ServiceModel.AddressAlreadyInUseException: The TransportManager failed to listen on the supplied URI using the NetTcpPortSharing service: the URI is already registered with the service.

As the self-hosted WCF Service (runs as a running process [Windows Service])] has used port number XXXX already, when Visual Studio attempts to fire the WCF Service Host again on the same port, the conflict occurs as the URI has been used up already.

Starting the Net.Tcp Port Sharing Service did not help either.

The work around to debug this issue in steps:
1.) Unload the project that contains the code for the host.
2.) Attach the process to the windows/self-hosting service. (At this point, WCF Service Host does not complain that the URI has been used as it attaches to the running self-hosting service instance)
3.) Reload the unloaded project and set breakpoints to step through code.

Tuesday, October 27, 2009

Unable to detect external usb hard drive on Windows Server 2003

I have been facing this issue with my external hard disk for quite some time.
Even though I manage to fix it in one way or the other every time, I just wanted to write it down this time so that it will be useful for some one else with the same problem (or may be me the next time).

I have a Maxtor Basics Portable USB External Drive (320 GB).
By default, no external drives are supported (nor very compatible in practice) in server operating systems such as Windows 2000 Server, Windows 2003 Server, or Windows 2008 Server.

So when a new USB Hard drive is connected, even though the hardware is recognized, I am not able to see the drive in the list of drives My Computer.

Open up "Computer Management" screen in Windows 2003 and go to "Disk Management" found under "Storage".



So its evident that the hard disk is recognized but Windows 2003 has not given it a drive letter. In order to get one, right click on the disk, click on "Change drive letters and paths". In the next screen, click on "Add" and give a drive letter as desired.



I am now able to see the new drive :)

Wednesday, October 21, 2009

Firing HttpModule on a file that does not exist physically

I am not going to explain what an HttpModule is and how to create one. There are hundreds of posts in Google that explain the same with nice screen shots.

This article here is a good example for some one who just started to learn about them

I knew that a HttpModule when configured properly has to intercept each and every request that reaches the web app it has been set on. But by default, the HttpModule fires only if the file exists on the server physically.

With a configuration change in IIS, I had been able to fire the HttpModule for every request irrespective of whether the file exists or not physically.

Step-1
Open up the properties of website and go to the tab "Home Directory"


Step-2
Click "Configuration", add a wildcard application map as shown below(aspnet_isapi.dll). Make sure that "Verify that file exists" is checked off

Debugging web based infopath forms

Recently a colleague who has been working on Infopath 2007 for quite long time has stated that its not possible to debug web-based infopath at all.

I believe that he came to that conclusion due to the fact that Visual Studio Tools for Applications (VSTA) does not show the option "Attach to debugger".

May be there are few others in the same boat as I was (I had been with the same opinion until my friend Sandeep Nahta said that web-based Infopath forms are very likely debuggable). After a bit of research, it has been evident that debugging web-based Infopath forms is not a big deal.

Follow the below steps:
1) Ensure the InfoPath Form Template is compiled in the Debug Configuration so the XSN includes the symbol (PDB) file. (From the Project menu choose Project Properties .Ensure the Configuration option is set to Active(Debug)/Debug)
2) Open the local copy of the code file (make sure not to open the csproj file created by Infopath. Just open the .cs file ONLY) in Visual Studio and set a breakpoint
3) Attach to the appropriate w3wp process
4) Execute the process that will cause the breakpoint to be hit

Ref: http://blogs.msdn.com/infopath/archive/2006/11/24/debugging-browser-forms.aspx

Monday, October 5, 2009

How to add web parts to publishing page layouts and make them available to all the pages through code?

Scenario:
How to add web parts to publishing page layouts and make them available to all the pages through code?

Explanation:
So whats the big deal? What is so special? This is the question that strikes in one's mind after reading the title of this post.

Well.. when custom web parts are added to publishing pages that are created from publishing page layouts, until the page is checked out, the web parts wont be displayed. (SharePoint thinks that the web parts are unsafe until the page is checked out for the first time even after strong naming them and marking them as safecontrols, etc..)

There are various ways to do this. Other than the one I am going to describe below, I have seen a project in which the web parts have been specified in the site definition thus marking the controls are safe.

To fix this issue, we have created a custom control that resides on the page layout which takes care of carrying out operations on page such as adding, deleting, moving and setting properties on web parts.

Code:
In the code-behind of the page layout add the pagelayout control in the OnInit() as below:
/// <summary>
/// Registers page layout control that adds web parts to the page.
/// </summary>
/// <param>Virtual path of XML File that contains web part information for the page</param>
/// <returns></returns>
protected void AddWebPartsToPage(String webpartsXMLFileUrl)
{
try
{
PageLayoutControl plc = new PageLayoutControl(webpartsXMLFileUrl);
this.Page.Controls.Add(plc);
}
catch (Exception exception)
{
if (ExceptionPolicy.HandleException(exception, "Iti Exception"))
{
}
}
}

Sample Xml that goes in the webpartsXMLFileUrl
<WebParts LastModified="9/5/2008">
<WebPartAction>
<action>Delete</action>
<typeName>WebPart1.WebPart</typeName>
<zoneIndex>0</zoneIndex>
</WebPartAction>
<WebPartAction>
<action>Delete</action>
<typeName>WebPart2.WebPart</typeName>
<zoneIndex>1</zoneIndex>
</WebPartAction>
<WebPartAction>
<assemblyName>WebPart1, Version=[Version], Culture=neutral, PublicKeyToken=[Token]</assemblyName>
<className>WebPart1.WebPart</className>
<zoneID>RightTop</zoneID>
<zoneIndex>0</zoneIndex>
<typeName></typeName>
<action>Add</action>
<Properties>
<!-- Properties for the web part go here -->
<!-- Example -->
<Property Key="Title" Type="string" Value="WebPart1 Title"/>
<Property Key="ChromeState" Type="PartChromeState" Value="Normal"/>
</Properties>
</WebPartAction>
<WebPartAction>
<assemblyName>WebPart2, Version=[Version], Culture=neutral, PublicKeyToken=[Token]</assemblyName>
<className>WebPart2.WebPart</className>
<zoneID>RightTop</zoneID>
<zoneIndex>1</zoneIndex>
<typeName></typeName>
<action>Add</action>
<Properties>
<!-- Properties for the web part go here -->
<Property Key="Title" Type="string" Value="WebPart1 Title"/>
<Property Key="ChromeState" Type="PartChromeState" Value="Normal"/>
</Properties>
</WebPartAction>
</WebParts>

And finally the PageLayoutControl code:
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;
using System.Diagnostics;
using Microsoft.SharePoint.Portal.WebControls;
using System.Collections;
using System.Reflection;
using System.ComponentModel;
using System.Data;
using System.IO;

using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using System.Runtime.Remoting.Contexts;

[XmlRoot("PageLayoutControl")]
public class PageLayoutControl : WebControl
{
//constants for property simple property setter value tag replacements
public const string PSV_DOMAIN = "[DOMAIN]";
public const string PSV_ACCOUNTNAME = "[ACCOUNTNAME]";
public const string PSV_MACHINE = "[MACHINE]";
public const string PSV_LOGIN = "[LOGIN]";

#region Properties
private String _fileLocation;
public String FileLocation
{
get
{
return _fileLocation;
}
set
{
_fileLocation = value;
}
}
#endregion

#region Constructor
public PageLayoutControl(String fileLocation)
{
this.FileLocation = fileLocation;
}
#endregion

#region Overriding Events

/// <summary>
/// Overriding the onLoad event of Custom Control.
/// This control is executed with Header place holder is executed in the Page Layout.
/// This code executes once on the page, It checks whether the code is executed
/// on the page, and if not it executes and adds the key to page for this code execution.
/// <param name="e" type="EventArgs">e</param>
/// <returns></returns>
/// </summary>

protected override void OnLoad(EventArgs e)
{
//do the base load functions
base.OnLoad(e);

//look to see if our web has had this stuff run yet
const string KEY_CHK = "PageLayoutControl";

//generic dictionary to store the list of webparts we're going to work 
//with and what we'll do with each; Steve likes generics!  :-)
Dictionary<string, WebPartAction> wpList = null;

//web part action class to store info about web parts that we're changing
WebPartAction wpa = null;

//webpart class that will be used in the action class
System.Web.UI.WebControls.WebParts.WebPart xWp = null;

//assembly we'll use to load web parts we are adding
Assembly wpAsm = null;

//serializer to convert the xml file into objects
XmlSerializer slz = null;

try
{
//get the current web; not using "using" because we don't want to kill
//the web context for other controls that need it

SPWeb curWeb = SPContext.Current.Web;

//turn on unsafe updates so we can make changes on a GET
curWeb.AllowUnsafeUpdates = true;


SPFile oPageLayout = curWeb.GetFile(SPContext.Current.File.Url);


//now that we have the file, read it into an xml document
XmlDocument xDoc = new XmlDocument();
string path = Context.Server.MapPath(_fileLocation);
xDoc.Load(path);

//compare the web part modified dates with the page
string pageWebPartActionsLastMod = string.Empty;
bool processWebPartActionsXml = true;
if (oPageLayout.Properties.ContainsKey(KEY_CHK))
if (xDoc.SelectSingleNode("WebParts") != null)
if (xDoc.SelectSingleNode("WebParts").Attributes["LastModified"] != null)
{
pageWebPartActionsLastMod = xDoc.SelectSingleNode("WebParts").Attributes["LastModified"].InnerText;

if (oPageLayout.Properties[KEY_CHK].ToString() == pageWebPartActionsLastMod)
processWebPartActionsXml = false;
}

//conditional block for processing webpart XML actions file
if (processWebPartActionsXml)
{
//check the page out
if (oPageLayout.CheckOutStatus == SPFile.SPCheckOutStatus.None)
SPSecurity.RunWithElevatedPrivileges(delegate { oPageLayout.CheckOut(); });

try
{

//create a hashtable to store our web parts
wpList = new Dictionary<string, WebPartAction>();

//create a new serializer
slz = new XmlSerializer(typeof(WebPartAction));

int webpartCount = 0; 
//enumerate through the child nodes
foreach (XmlNode xNode in xDoc.FirstChild.ChildNodes)
{
try
{
//try serializing each one
wpa = slz.Deserialize(new System.IO.StringReader(xNode.OuterXml)) as WebPartAction;
}
catch (Exception exception)
{
//set our reference to null so we don't try and do something with
//a version that was successfully serialized previously
wpa = null;
}


//make sure we serialized, and also make sure this is an Add
if (wpa != null)
{
switch (wpa.action)
{
case WebPartAction.ActionType.Add:

//now try loading the assembly
try
{
wpAsm = Assembly.Load(wpa.assemblyName);
}
catch (Exception exception)
{
//set our reference to null so we don't try and do something with
//a version that was successfully loaded previously
wpAsm = null;
}

if (wpAsm != null)
{
//try creating an instance of the class
try
{
xWp = (System.Web.UI.WebControls.WebParts.WebPart)wpAsm.CreateInstance(wpa.className);
}
catch (Exception exception)
{
//set our reference to null so we don't try and do something with
//a version that was successfully loaded previously
xWp = null;
}

//plug it into our class
if (xWp != null)
{
//enumerate and add properties to part
SetWebPartProperties(wpa, xWp);

//add part to class
wpa.wp = xWp;
}
}

//add it to the hashtable
wpList.Add(Guid.NewGuid().ToString(), wpa);
break;
default:
//for delete, move or SetProperties we want to capture 
//the type name and plug it into the hash array
webpartCount++;
if ( !wpList.ContainsKey(wpa.typeName.ToString() + webpartCount.ToString()))
wpList.Add(wpa.typeName.ToString() + webpartCount.ToString(), wpa);
break;
}
}
}

//get the web part manager
SPLimitedWebPartManager theMan = oPageLayout.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
webpartCount = 0;
foreach (System.Web.UI.WebControls.WebParts.WebPart wp in theMan.WebParts)
{
//Increments webpart counter........
webpartCount++;
//check each web part to see if matches our typeName
if (wpList.ContainsKey(wp.GetType().ToString() + webpartCount.ToString()))
wpList[wp.GetType().ToString() + webpartCount.ToString()].wp = wp;
}

//now enumerate items in hash; can't do it in WebPart collection 
//on SPLimitedWebPartManager or it fails
foreach (string key in wpList.Keys)
{
wpa = wpList[key];
if (wpa.wp != null)
{
switch (wpa.action)
{
case WebPartAction.ActionType.Delete:
theMan.DeleteWebPart(wpa.wp);
break;
case WebPartAction.ActionType.Move:
theMan.MoveWebPart(wpa.wp, wpa.zoneID, int.Parse(wpa.zoneIndex));
theMan.SaveChanges(wpa.wp);
break;
case WebPartAction.ActionType.Add:
theMan.AddWebPart(wpa.wp, wpa.zoneID, int.Parse(wpa.zoneIndex));
break;
case WebPartAction.ActionType.SetProperties:
SetWebPartProperties(wpa, wpa.wp);
theMan.SaveChanges(wpa.wp);
break;
}
}

}

//add our key to the property bag so we don't run our provisioning code again
if (oPageLayout.Properties.Contains(KEY_CHK))
{
oPageLayout.Properties[KEY_CHK] = pageWebPartActionsLastMod;
}
else
{
oPageLayout.Properties.Add(KEY_CHK, pageWebPartActionsLastMod);
}


oPageLayout.Update();

// for making the page appear in edit mode when first provisioned.

////check the page in and publish
// to be executed when migrated pages which should have been published before.
if (oPageLayout.MajorVersion >= 1 )
{
SPSecurity.RunWithElevatedPrivileges(delegate
{
try
{
oPageLayout.CheckIn(string.Format(" -- checked in on {0}", DateTime.Now));
oPageLayout.Publish(string.Format(" -- published on {0}", DateTime.Now));
}
catch (Exception ex)
{ }
});
}
// To be executed when new site is create with a layout as default page.
if (oPageLayout.Versions.Count == 1 && oPageLayout.MinorVersion == 1 && oPageLayout.CheckOutStatus == SPFile.SPCheckOutStatus.None )
{
SPSecurity.RunWithElevatedPrivileges(delegate
{
try
{
oPageLayout.CheckOut(); 
}
catch (Exception ex)
{ }
});
}
curWeb.AllowUnsafeUpdates = false;


Context.Response.Clear();
Context.Response.Redirect(oPageLayout.Name + "?ControlMode=Edit&DisplayMode=Design", false);

//potentially delete our xml manifest file that tells us what to do
//would be cool if we could just remove ourselves from the page
//or maybe we stay there so we could be activated again in the future?? That
//would be an interesting idea...
}

catch (Exception exception)
{
if (ExceptionPolicy.HandleException(exception, "Iti Exception"))
{

}
}
}

}
catch (Exception exception)
{
}
}
#endregion

#region Custom Methods

/// <summary>
/// This method sets the WebPart Properties before placing them on the page.
/// It checks if the Web Part Properties are specified in the Xml file.
/// Property Name "Title" and "Associated List Name".
/// <param name="wpa" type="WebPartAction">wpa</param>
/// <param name="xWp" type="System.Web.UI.WebControls.WebParts.WebPart">xWp</param>
/// <returns></returns>
/// </summary>

private void SetWebPartProperties(WebPartAction wpa, System.Web.UI.WebControls.WebParts.WebPart xWp)
{
//check to see if there are any properties; if there are zero it won't say
//zero, it will say null (unlike vb.net)
if (wpa.Properties == null)
return;

//enumerate and add properties to part
for (int p = 0; p < wpa.Properties.Property.Length; p++)
{
try
{
// checks the "Type" attribute of property and calls the appropriate method to get property value.
switch (wpa.Properties.Property[p].Type.ToString().ToLower())
{
case "string":
xWp.GetType().GetProperty(wpa.Properties.Property[p].Key).SetValue(xWp,
GetPropertySetterValue(wpa.Properties.Property[p].Value), null);
break;
case "partchromestate":
xWp.GetType().GetProperty(wpa.Properties.Property[p].Key).SetValue(xWp,
GetPropertySetterPartChrome(wpa.Properties.Property[p].Value), null);
break;
}

}

catch (Exception exception)
{
if (ExceptionPolicy.HandleException(exception, "Iti Exception"))
{
//  throw;
}
}

}
}

/// <summary>
/// This is a property getter method which gets the WebPart Property
/// of Type string. This methods set the property for all string type properties.
/// Property Name "Title" and "Associated List Name".
/// <param name="Value" type="string">Value</param>
/// <returns name="ret" type="string">ret</returns>
/// </summary>

private string GetPropertySetterValue(string Value)
{
try
{
string ret = Value;

switch (Value)
{
case PSV_ACCOUNTNAME:
ret = Environment.UserName;
break;
case PSV_DOMAIN:
ret = Environment.UserDomainName;
break;
case PSV_MACHINE:
ret = Environment.MachineName;
break;
case PSV_LOGIN:
ret = Environment.UserDomainName + "\\" + Environment.UserName;
break;
default:
break;
}

return ret;
}
catch (Exception exception)
{
if (ExceptionPolicy.HandleException(exception, "Iti Exception"))
{
//  throw;
}
return null;
}

}

/// <summary>
/// This is a property getter method which gets the WebPart Property
/// of Type PartChromeState. Its has two states Normal / Minimized
/// <param name="Value" type="string">Value</param>
/// <returns name="chState" type="PartChromeState">chState</returns>
/// </summary>

private PartChromeState GetPropertySetterPartChrome(string Value)
{
try
{
PartChromeState chState = PartChromeState.Normal;

// checks if its find the xml attribute "Minimized"
// Sets the WebPart State to Minimized.
if (Value == "Minimized")
{
chState = PartChromeState.Minimized;
}
return chState;
}
catch (Exception exception)
{
return PartChromeState.Normal;
}
}

#endregion
}

#region WebPart Property Class

/// <summary>
/// Class defines the WebPart Actions the are required to place 
/// the WebParts by default when the instance of Page Layout is created.
/// These actions corresponds to xml elements that are nested under <PageLayout> </PageLayout> element.
/// </summary>

public class WebPartAction
{
/// <summary>
/// Enumerating the Actions that can be applied to WebParts.
/// </summary>
public enum ActionType
{
Delete,
Add,
Move,
SetProperties
}

#region WebPartAction Properties

[XmlIgnore()]
public System.Web.UI.WebControls.WebParts.WebPart wp = null;

/// <summary>
/// Specify Assembly Name.
/// </summary>
[XmlElement(Form = XmlSchemaForm.Unqualified)]
public string assemblyName = string.Empty;

/// <summary>
/// Specify Class Name.
/// </summary>
[XmlElement(Form = XmlSchemaForm.Unqualified)]
public string className = string.Empty;

/// <summary>
/// Specify Web Part Zone ID.
/// </summary>
[XmlElement(Form = XmlSchemaForm.Unqualified)]
public string zoneID = string.Empty;

/// <summary>
/// Specify Web Part Zone Index.
/// using a string to greatly simplify potential errors during deserialization.
/// </summary>
[XmlElement(Form = XmlSchemaForm.Unqualified)]
public string zoneIndex = "0";

/// <summary>
/// Specify Type Name.
/// </summary>
[XmlElement(Form = XmlSchemaForm.Unqualified)]
public string typeName = string.Empty;

/// <summary>
/// Specify Web Part Action (Add, Delete, Move or Set Properties).
/// </summary>
[XmlElement(Form = XmlSchemaForm.Unqualified)]
public ActionType action;

/// <summary>
/// Specify Web Part Properties.
/// </summary>
[XmlElement(Form = XmlSchemaForm.Unqualified)]
public PropertyRoot Properties;

#endregion

#region Constructors

/// <summary>
/// Parameterless constructor for WebPart Action Class. 
/// <param></param>
/// <returns></returns>
/// </summary>
public WebPartAction()
{
//parameter-less constructor needed for serialization
}

/// <summary>
/// Parameterized constructor for WebPart Action Class. 
/// Used to deserilaize the xml with WebPart Actions defined. 
/// <param name="wp" type="System.Web.UI.WebControls.WebParts.WebPart">wp</param>
/// <param name="action" type="ActionType">action</param>
/// <returns></returns>
/// </summary>
public WebPartAction(System.Web.UI.WebControls.WebParts.WebPart wp, ActionType action)
{
this.wp = wp;
this.action = action;
}

/// <summary>
/// Parameterized constructor for WebPart Action Class. 
/// Used to deserilaize the xml with WebPart Actions, Zone ID and Zone Index defined. 
/// <param name="wp" type="System.Web.UI.WebControls.WebParts.WebPart">wp</param>
/// <param name="action" type="ActionType">action</param>
/// <param name="zoneID" type="string">zoneID</param>
/// <param name="zoneIndex" type="string">zoneIndex</param>
/// <returns></returns>
/// </summary>
public WebPartAction(System.Web.UI.WebControls.WebParts.WebPart wp, ActionType action, string zoneID, string zoneIndex)
{
this.wp = wp;
this.action = action;
this.zoneID = zoneID;
this.zoneIndex = zoneIndex;
}

#endregion


#region WebPart Property Class

/// <summary>
/// Class specify the webpart properties.
/// Get the array of properties in the form of xml elements,
/// with its attributes as property values.
/// </summary>

public class PropertyRoot
{
[XmlElement(Form = XmlSchemaForm.Unqualified)]
public OneProperty[] Property;
}

#endregion

#region Property Attributes Class

/// <summary>
/// Class defines the webpart property attributes.
/// Three attributes of property can be defined,
/// <Key> <Type> <Value>
/// </summary>
public class OneProperty
{
[XmlAttribute(Form = XmlSchemaForm.Unqualified)]
public string Key = string.Empty;

[XmlAttribute(Form = XmlSchemaForm.Unqualified)]
public string Type = string.Empty;

[XmlAttribute(Form = XmlSchemaForm.Unqualified)]
public string Value = string.Empty;
}
#endregion
}
#endregion

Wednesday, September 30, 2009

The solution file located at 'xxx.wsp' does not appear to be a valid upgrade

Scenario:
Recently I have encountered the following error when I tried to upgrade my solution.

Explanation:
Usually this error means either the feature scope has been changed.

There have been theories that addition of a new feature(s) may be a cause for the same, but I have not experienced that so far. Adding a new feature to an existing solution and upgrading the solution does not always throw errors but simply won't list the new feature that is added to the solution. In order to have the new feature shown up in the feature gallery, a redeploy of the solution is required usually.
A good friend of mine Sandeep Nahta has written a good blog post describing the same

Finally it has been found that the error was due to the missing safecontols declaration node in manifest file. As one of the features in our solution has been scoped for webapplication and no safecontols were defined

  <Assemblies>
    <Assembly Location="Assembly.dll" DeploymentTarget="GlobalAssemblyCache" />
  </Assemblies>

After modifying the manifest file like below, the error disappeared:
  <Assemblies>
    <Assembly Location="Assembly.dll" DeploymentTarget="GlobalAssemblyCache">
      <SafeControls>
        <SafeControl Assembly="Assemmbly info" TypeName="*" Safe="True" />
      </SafeControls>
    </Assembly>
  </Assemblies>


If no new feature has been added to the solution or if the scope has not been changed, it may not always be a very good idea to retract, delete, install and deploy the solution again as any web app scoped feature(s) that may be present in the solution would be deactivated due to the same and therefore have to be activated again.

Annoying "Are you sure you want to navigate away from this page?" message in SharePoint pages in IE8

"Are you sure you want to navigate away from this page?"

Many users have complained about this and therefore be prepared to see this annoying message in SharePoint pages where you have custom field controls placed.

Found a temporary work around for this here

Monday, September 21, 2009

Copy web.config settings using a SharePoint Feature

Scenario:
Copy web.config settings using a SharePoint Feature

Explanation:
For most of the projects, it is usually required to add few web.config entries.
Adding them in the WFE servers manually is prone to mistakes.

Deploying them as a feature ensures that its not only clean but also eases the job of administrators if a new WFE server is added in the future.

Lets take the scenario where we had to update web.config with entries for Telerik Controls

Code:
//Feature.xml

<?xml version="1.0" encoding="utf-8"?>
<Feature
Id="[ID]"
Title="Telerik Controls"
Description="This feature adds the required Telerik Web.Config entries and assosiated dlls"
Version="1.0.0.0"
Scope="WebApplication"
Hidden="false"
ImageUrl="TelerikControls\img.jpg"
ReceiverAssembly="TelerikControls, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d6ff03c5a94c295d"
ReceiverClass="TelerikControls.TelerikControlsFeatureReceiver"
xmlns="http://schemas.microsoft.com/sharepoint/">
<ElementManifests>
<ElementFile Location="Web.Config" />
</ElementManifests>
</Feature>


//Web.Config

<configuration>
<!-- SafeControls for Telerik -->
<SharePoint>
<SafeControls>
<SafeControl Assembly="Telerik.Web.Design, Version=2009.2.701.35, Culture=neutral, PublicKeyToken=121fae78165ba3d4" Namespace="Telerik.Web.Design" TypeName="*" Safe="True" />
<SafeControl Assembly="Telerik.Web.UI, Version=2009.2.701.35, Culture=neutral, PublicKeyToken=121fae78165ba3d4" Namespace="Telerik.Web.UI" TypeName="*" Safe="True" />
</SafeControls>
</SharePoint>
<system.web>
<httpHandlers>
<add verb="*" validate="false" path="Telerik.Web.UI.DialogHandler.axd" type="Telerik.Web.UI.DialogHandler, Telerik.Web.UI, Version=2009.2.701.35, Culture=neutral, PublicKeyToken=121fae78165ba3d4" />
</httpHandlers>
</system.web>
</configuration>


//FeatureReceiver

using System.Xml;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

namespace TelerikControls
{
public class TelerikControlsFeatureReceiver : SPFeatureReceiver
{

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
string xPath;
SPWebApplication webApp;
WebConfigModifications webConfigMods;
XmlDataDocument xmlDoc;

webApp = (SPWebApplication)properties.Feature.Parent;
webConfigMods = new WebConfigModifications();

//load the web.config settings from the feature root
xmlDoc = new XmlDataDocument();
xmlDoc.Load(string.Format("{0}\\{1}", properties.Feature.Definition.RootDirectory, "web.config"));

//configuration/SharePoint/SafeControls
xPath = "configuration/SharePoint/SafeControls";
AddWebConfigNodes(xPath, ref xmlDoc, ref webConfigMods, ref webApp);

//configuration/system.web/httpHandlers
xPath = "configuration/system.web/httpHandlers";
AddWebConfigNodes(xPath, ref xmlDoc, ref webConfigMods, ref webApp);

//set full trust
SPWebConfigModification modification = new SPWebConfigModification("level", "configuration/system.web/trust");
modification.Owner = "WebConfigTrust";
modification.Sequence = 0;
modification.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureAttribute;
modification.Value = "Full";
webApp.WebConfigModifications.Add(modification);

webApp.Farm.Servers.GetValue<SPWebService>().ApplyWebConfigModifications();
webApp.Update();

}


private void AddWebConfigNodes(string xPath, ref XmlDataDocument xmlDoc, ref WebConfigModifications webConfigMods, ref SPWebApplication webApp)
{
foreach (XmlNode node in xmlDoc.SelectSingleNode(xPath))
if (node.NodeType != XmlNodeType.Comment)
webConfigMods.AddWebConfigNode(webApp, xPath, node, node.Attributes);
}

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;
WebConfigModifications webConfigMods = new WebConfigModifications();
webConfigMods.RemoveWebConfigNodes(webApp);
webApp.Farm.Servers.GetValue<SPWebService>().ApplyWebConfigModifications();
webApp.Update();
}

public override void FeatureInstalled(SPFeatureReceiverProperties properties)
{
/* no op */
}
public override void FeatureUninstalling(SPFeatureReceiverProperties properties)
{
/* no op */
}
}
}


//Helpers

using System.Xml;
using System.Text;
using Microsoft.SharePoint.Administration;
using System.Collections.ObjectModel;

namespace TelerikControls
{
public class WebConfigModifications
{

public string Owner
{
get
{
return this.GetType().FullName;
}
}

private string GetWebConfigModName(string nodeName, XmlAttributeCollection attributes)
{
StringBuilder webConfigModName = new StringBuilder(nodeName);


foreach (XmlAttribute attribute in attributes)
{
webConfigModName.Append(string.Format("[@{0}=\"{1}\"]", attribute.Name, attribute.Value));
}


return webConfigModName.ToString();
//return string.Format("add[@key=\"{0}\"]", key);

}

private string GetWebConfigModValue(string nodeName, XmlAttributeCollection attributes)
{
XmlDataDocument xDoc = new XmlDataDocument();
XmlAttribute newAttribute;
XmlNode modValueNode = xDoc.AppendChild(xDoc.CreateElement(nodeName));
foreach (XmlAttribute attribute in attributes)
{
newAttribute = xDoc.CreateAttribute(attribute.Name);
newAttribute.Value = attribute.Value;
modValueNode.Attributes.Append(newAttribute);
}
return string.Format("{0}\n", modValueNode.OuterXml);
}

private string GetFirstNodeName(ref XmlDataDocument xDoc)
{
string xPath = xDoc.FirstChild.Name;

//we don't want to process the xml declaration node
if (xPath == "xml")
{
if (xDoc.FirstChild.NextSibling != null)
{
xPath = xDoc.FirstChild.NextSibling.Name;
}
else
{
xPath = string.Empty;
}
}

return xPath;
}




/// <summary>
/// Adds the key/value pair as an appSettings entry in the web application's
/// SPWebConfigModification collection
/// </summary>
/// <param name="webApp">Current web application context</param>
/// <param name="key">appSettings node key</param>
/// <param name="value">appSettings node value</param>
public void AddWebConfigNode(SPWebApplication webApp, string webConfigModxPath, XmlNode node, XmlAttributeCollection attributes)
{
SPWebConfigModification webConfigMod;
string webConfigModName;

webConfigModName = GetWebConfigModName(node.Name, attributes);
webConfigMod = new SPWebConfigModification(webConfigModName, webConfigModxPath);
webConfigMod.Owner = this.Owner;
webConfigMod.Sequence = 0;
webConfigMod.Type = SPWebConfigModification.SPWebConfigModificationType.EnsureChildNode;
webConfigMod.Value = node.OuterXml;

webApp.WebConfigModifications.Add(webConfigMod);
}

/// <summary>
/// Removes the key from the appSettings the web application's
/// SPWebConfigModification collection
/// </summary>
/// <param name="webApp">Current web application context</param>
/// <param name="key">appSettings node key</param>
public void RemoveWebConfigNodes(SPWebApplication webApp)
{
Collection<SPWebConfigModification> collection;
SPWebConfigModification[] tempCollection;
string webConfigModName;
int iStartCount;
SPWebConfigModification webConfigMod;

collection = webApp.WebConfigModifications;
tempCollection = new SPWebConfigModification[collection.Count];
collection.CopyTo(tempCollection, 0);
iStartCount = collection.Count;

// Remove any modifications that were originally created by the owner.
for (int c = iStartCount - 1; c >= 0; c--)
{
webConfigMod = collection[c];

if (webConfigMod.Owner == this.Owner)
collection.Remove(webConfigMod);
}
}
}
}

Setting properties for iFrame from code behind

Scenario:
I have an iFrame in the markup. I need to set properties of the iFrame through code

Code:
//Markup

<iframe src ="[Url Here]" runat="server" frameborder=0 id="iFrame">
<p>Your browser does not support iframes.</p>
</iframe>

//Code Behind

protected System.Web.UI.HtmlControls.HtmlGenericControl iFrame;

iFrame.Attributes["src"] = "[The Url]";

Attaching an event handler to a list though code in SharePoint 2007

Scenario:
Attaching an event handler to a list in SharePoint 2007

Code:

SPWeb web = properties.Feature.Parent as SPWeb;
SPList list = web.Lists["[List Name]"];
String assemblyName = "[Assembly Name],Version=[Version],Culture=neutral,PublicKeyToken=[PublicKeyToken]";
String className = "EventReceiverClassName";
list.EventReceivers.Add(SPEventReceiverType.ItemUpdated, assemblyName, className);
list.Update();

An easy way to find out whether a page is approved or published through code in SharePoint 2007

Scenario:
An easy way to find out whether a page is approved or published through code in SharePoint 2007

Code:

if (page.ModerationInformation.Status == SPModerationStatusType.Approved)

Seperate DisplayMode Controls from EditMode Controls in a SharePoint field control

Scenario:
Its not very uncommon to have a field control separate display mode controls from edit mode controls. The approach I follow is to seperate them in CreateChildcontrols()

Code:
if (this.ControlMode == SPControlMode.Edit || this.ControlMode == SPControlMode.New)
{
//CreateEditModeControls();
//Code for adding Edit Mode Controls here
}
else
{
//CreateDisplayModeControls();
//Code for adding Display Mode Controls here
}

Add CssFiles, EditModes, ToolFiles and DialogHandler to Telerik RadEditorUrl

Scenario:
When working with Telerik's RadEditor, one may need to set certain properties for the control such as ToolsFile, EditModes, CssFiles, DialogHandlerUrl etc.. Its handy to have the below:

Code:

RadEditor.ToolsFile = "~/_layouts/[FolderName]/[ToolsXmlFileName].xml";
RadEditor.EditModes = EditModes.Html | EditModes.Design;
RadEditor.CssFiles.Add("~/_layouts/[FolderName]/CSS/PageLayouts/RadEditorCss.css");
RadEditor.DialogHandlerUrl = "~/Telerik.Web.UI.DialogHandler.axd";

How to add Custom Quick Access Buttons to the Page Editing Toolbar in MOSS 2007 Publishing Page

Scenario:
How to add Custom Quick Access Buttons to the Page Editing Toolbar in SharePoint Server 2007 Publishing Page?

Explanation:
Quick Access buttons in the Page Editing toolbar are founded on the concept of actions. Actions, also known as console actions, can be created by developers to provide a specific type of control.

First, create an action by creating a class that inherits from the Microsoft.SharePoint.Publishing.WebControls.EditingMenuActions.ConsoleAction class. The custom class then overrides many methods and properties to define the conditions when the button should appear. For example, the UserRights property is a bitwise flag that specifies what permissions the user must have to see the button. The RequiresStates property is another bitwise flag that dictates under what conditions the button should appear.

After you create the button class, and sign and deploy the assembly to the global assembly cache, the next step is to make the SharePoint publishing site aware of the new button. The Quick Access buttons on the Page Editing toolbar are defined by the QuickAccess.xml file, located in the path [..]\12\TEMPLATE\LAYOUTS\EditingMenu. However, developers should not modify this file. Instead, the QuickAccess.xml file points to the CustomQuickAccess.xml file in the Master Page Gallery of each SharePoint publishing site collection. This file contains a reference that points to the custom console action class and an assembly that contains the class, very much like the <% @Register %> directive in an ASPX or ASCX file. After the reference, the CustomQuickAccess.xml file includes a section declaring the console action

Code:
The below code is used to add a "Preview" button to the Page Editing Toolbar
public class PublishingPagePreview : ConsoleAction
{
private string _imageUrl;

public override string DisplayText
{
get
{
return "Preview";
}
set
{
base.DisplayText = value;
}
}

public override SPBasePermissions UserRights
{
get { return SPBasePermissions.EmptyMask; }
}

public override AuthoringStates RequiredStates
{
get { return AuthoringStates.EditingMenuEnabled; }
}

public override string ImageUrl
{
get
{
if (string.IsNullOrEmpty(_imageUrl)) return "~/_layouts/images/prvw_imf.gif";
else return _imageUrl;
}
set { _imageUrl = value; }
}

public override string NavigateUrl
{
get
{
PublishingWeb pubWeb = PublishingWeb.GetPublishingWeb(SPContext.Current.Web);
return "javascript:if(!g_previewRequested){g_previewRequested=true;__doPostBack('ctl00$ctl07$saPreview_CmsActionControl','previewPage');}";
}
}
}

Xml:
Go to Site Settings > Master Page Gallery > Editing Menu Folder
Edit the CustomQuickAccess.xml, check in the file and make sure to approve it.
<?xml version="1.0" encoding="utf-8" ?>
<Console>
<references>
<reference TagPrefix="customButton" assembly="[AssemblyName], Version=1.0.0.0, Culture=neutral, PublicKeyToken=[Token]" namespace="[NameSpace]" />
</references>
<structure>
<ConsoleNode Sequence="198" Action="customButton:PublishingPagePreview" DisplayText="Preview" UseResourceFile="false" ID="qaPreviewAllData"/>
</structure>
</Console>

References: http://msdn.microsoft.com/en-us/library/bb986730.aspx

Thursday, September 17, 2009

Edit Page disabled in Page Layout

"Edit Page" option in my page layout is no more enabled / available.
After further examination, it has been found that the edit page option has been made unavailable due to the fact that there are no SharePoint controls in the display mode.

In my case, I am loading an iFrame in the display mode of the page which loads an aspx page.

I fixed this issue by adding a SharePoint control and by marking it as hidden.

Code:

<div style="display:none;">
<SharePointWebControls:FieldValue runat="server" id="TitleFieldValue" FieldName="Title"/>
</div>
<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Display" SuppressTag="true">
<!-- Code here -->
</PublishingWebControls:EditModePanel>
<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Edit" SuppressTag="true">
<!-- Code here -->
</PublishingWebControls:EditModePanel>

How to seperate edit mode controls and display mode controls in ASPX of a SharePoint publishing page layout?

Scenario:
It is possible to separate the edit mode controls from display mode controls from the aspx file of a SharePoint publishing page layout

Code:

<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Display">
<PublishingWebControls:EditModePanel runat="server" PageDisplayMode="Edit">

Tuesday, September 15, 2009

Anchor tag alteration by SharePoint in Custom Field

Scenario:
Anchor tag alteration by SharePoint in Custom Field

Explanation:
Yes that is right!!

SharePoint tries to clean up all anchor tags in fields even if its a custom multicolumn field. Many experienced SharePoint developers I have spoken to about this are not aware of this.

Surprisingly it seems very awkward that Microsoft has failed to generate the altered (cleaned up according to SharePoint) HTML according to the standards properly.

The problem has occurred when I tried to add the cleaned up HTML as a node to an XML file. The generated XML therefore is invalid.

I have noticed that SharePoint actually interferes with the content that is saved into a field. A quick work around I found was to replace the characters '>', '<' and '"' with their ASCII equivalents or any other character that may be able to replace them (like ' for "). Encode the data with your custom encoding mechanism, save the data into the field, when you retrieve the data back, make sure to decode it. Code:
content.Replace("<", "<").Replace(">", ">").Replace("\"", "'");

CSS Constants / Variables

It is very common to find repeated property values in a CSS stylesheet. CSS Variables allow authors to define variables that are reusable as property values anywhere in a stylesheet and queryable/modifiable through an extension of the CSS Object Model.

Why variables in CSS?

Many a times, any CSS developer would have felt that s/he is using repeated property values in a CSS stylesheet, for instance to make sure semantically different elements in a web page have a similar rendering and user experience. CSS does offer a way to group styles using groups of selectors, but we tend to neglect it more because of the fact that it’s difficult to maintain, decreases readability and of course semantically distinct elements rarely share all style rules.

The CSS Variables Definition

CSS Variables should be defined in an @variables at-rule. An @variable at-rule is composed of the '@' character followed by the 'variables' identifier, optional target media types (separated by commas) and a block of variable definitions. The definition of a variable must precede all style rules contained or imported in the stylesheet.

Usage Example

Using the value of a variable as the value or one of the values of a property in a CSS declaration should be achieved using the new functional notation var(). This function takes only one argument being the identifier being the name of the variable. The declaration becomes invalid if the variable does not exist.



One more way of declaring variables is by prefixing the variable name with '$'


/* Company Colours */
$blue='#369';
$green='#363';
$lgreen='#cfc';

ul#navigation
{
background:$blue;
color:#fff;
}

h1
{
border-bottom:1px solid $green;
}

or

@variables
{
oColor: #fefedb;
oBgColor: #ccc;
oMargin: 1em;
oPadding: 1em;
}
div#post div.entry
{
border: 1px solid #666;
font: normal normal normal 1em/1.6em "Lucida Grande", Lucida, Verdana, sans-serif;
margin: var(oMargin);
padding: var(oPadding);
color: var(oColor);
background-color: var(oBgColor);
}

Original Posts:
http://brajeshwar.com/2008/css-variables/
http://icant.co.uk/articles/cssconstants/

Tuesday, August 18, 2009

Field Control unable to call Validate method

When building custom field controls with custom web controls, if you are using Validate() method of the field control, its extremely important to make sure that the CausesValidation property of the control(s) that triggers postback inside the field control is set to true.

Tuesday, August 11, 2009

List Content Type vs Site Content Type

So what is the difference between both?

Update an existing content type that is being used in a list. (Add a new column, edit an existing column or remove one)

Go to the list settings. You will still see the old content type with out the newly added fields or still with the deleted fields. Delete the existing content type (Make sure that there are no list items that use this content type). Add the content type again from the list of existing content types. Also make sure that you remove any references to the columns (such as from views, etc..) and delete the list columns that have been changed. Add the content type again and you will see all the deleted columns again.

So what was the catch?
The content type that was modified was a site content type. SharePoint creates a list content type based on the site content type each time a content type is added to a list.

Friday, July 24, 2009

Custom logo and site name in SharePoint master page

Today when I was branding my master page, I had to change the default view of how the logo and site name are displayed in the SharePoint Blueband.master.

I started off with a copy of Blueband.master which has the below control that renders the image followed by the site name(url):

<sharepoint:aspmenu id="logoLinkId" runat="server" datasourceid="SiteMapDataSourceRoot" staticdisplaylevels="1" maximumdynamicdisplaylevels="0" accesskey="1" cssclass="logo">
</sharepoint:aspmenu>

As my company logo is a bit big, I wanted the image and site name to be displayed in seperate rows and I also wanted to apply my own styles to them.
The approach below from Heather Soloman's blog interested me.

<sharepoint:sitelogoimage id="onetidHeadbnnr0" logoimageurl="/_layouts/images/titlegraphic.gif" runat="server"/>
<asp:contentplaceholder id="PlaceHolderSiteName" runat="server">
<sharepoint:splinkbutton runat="server" navigateurl="~site/" id="onetidProjectPropertyTitle">
<sharepoint:projectproperty property="Title" runat="server">
</sharepoint:projectproperty>
</sharepoint:splinkbutton>
</asp:contentplaceholder>

But as I needed a bit more control, I finally ended up with my own code:

<asp:Image runat="server" id="XXX" ImageUrl="/_layouts/XXXMasterPages/images/xxxLogo.gif" />
<p class="xxxSiteNameContainer">
<SharePoint:SPLinkButton runat="server" NavigateUrl="~site/" id="onetidProjectPropertyTitle" CssClass="xxxSiteName">
<SharePoint:ProjectProperty ID="ProjectProperty1" Property="Title" runat="server" />
</SharePoint:SPLinkButton>
</p>

An error occurred during the processing of . Unknown server tag 'asp:ScriptManager'.

So I have changed the default.master for testing some thing and have forgotten to revert it back.
This is what I ended up with when I attempted to open Central Administration default.aspx page.
"An error occurred during the processing of . Unknown server tag 'asp:ScriptManager'"

So went back to the 12 hive Global folder and reverted the default.master to its original state.
Central Administration's default.aspx page uses default.master.

Wednesday, July 8, 2009

The first post

My intention behind starting this blog is to blog down the pit falls, frustations, nice to have's, fixes, features I encounter while developing applications in SharePoint, WCF, WF, CSS, AJAX, SQL Server ..... whatever I know which I feel will be of use for me and others in future.
Consider this as my scribble pad