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/