Saturday, September 12, 2009

Friday, July 31, 2009

Nice feature for deploying AJAX 3.5 web.config settings

I original downloaded this feature as I wanted to deploy the AJAX 3.5 web.config settings to the farm.

http://platinumdogs.wordpress.com/2009/07/20/sharepoint-feature-for-net-3-5-web-config-changes/

After downloading the VS solution and having a look around I decided to add all my web.config values to the webconfig.xml in this neat feature.

Deploying config values couldnt be easier!

Thursday, June 25, 2009

Check if SPList or SPView exists

One thing that the SharePoint team left out is a built-in way to see if a SPList or SPView exists.

So if you have the Dynamic library from ScottGu's Blog then you can setup an extension method or helper method with the following linq:

SPList

spweb.Lists.Cast().Any(list => string.Equals(list.Title, listName));

SPView

splist.Views.Cast().Any(view => string.Equals(view.Title, viewName));

both return a boolean

Monday, June 15, 2009

MOSS Search crawler not populating out of the box or custom metadata properties

One interesting issue we faced on my current ongoing project was that MOSS Search crawler would not popluate the out of the box or custom metadata properties, it would return results as if it was a HTTP crawl. This was only an issue on 1 farm, unfortunately this was the production farm, the UAT environment was working as expected.

After a colleague of mine searched the interent endlessly, finding others with the same issue but no answers, we logged a call with Microsoft and got a guy on live meeting who solved the issue in about 5 minutes.
When SharePoint creates the IIS Web Application it should place a HTTP Response Header called MicrosoftSharePointTeamServices with a version number as shown below:



In our case in production for whatever reason it did not create this header.
Adding the header manually got use up and running!

PortalSiteMapProvider randomly drops off pages

I built custom navigation for the http://nzte.govt.nz website using the PortalSiteMapProvider.
This is a powerful ally in the SharePoint Publishing Site world as it handles caching and security trimming. It also means that when you use one of the built-in instances of the PortalSiteMapProvider like CurrentNavSiteMapProvider you can use the navigation edit menu under Site Actions->Site Settings to hide/show sites and pages, change the order and add new links.

One thing that was unexepected was that every now and then the PortalSiteMapProvider would not return pages. We had content editors showing us screenshots of the menu showing only sites. This was easy to replicate by pressing F5 repeatedly until eventually it would happen (around 20-40 refreshes).
Now by default the propoerty IncludePages is set to PerWeb, so in attempt to fix the issue I added IncludePages="Always" to the web config as follows:

<add name="CurrentNavSiteMapProvider" .... includepages="Always" dynamicchildlimit="0">

but this didnt help.

The answer is to set the IncludePages property in your code block when you are building your navigation as follows:


PortalSiteMapProvider psmp = PortalSiteMapProvider.CurrentNavSiteMapProvider;
psmp.IncludePages = PortalSiteMapProvider.IncludeOption.Always; //resetting as it sometimes drops off
SiteMapNode siteMapNode = psmp.FindSiteMapNode(elevatedWeb.ServerRelativeUrl);
List leve1ItemList = new List();
for (int i = siteMapNode.ChildNodes.Count - 1; i >= 0; i--)
{
leve1ItemList.Add(siteMapNode.ChildNodes[i]);
}
leve1ItemList.Reverse();
level1Repeater.DataSource = leve1ItemList;
level1Repeater.DataBind();
}


One other thing is to set the DynamicChildLimit in the web config as by default it only displays 50 and if you have over 50 items it can also cause unexcept results like not displaying anything at all. As you can see above I have set it to 0 which means it has no limits.

Friday, May 15, 2009

Using LINQ in Sharepoint along side CAML

Using LINQ is a huge advantage when programming. It gives you great flexiblity with minimal code.
CAML is a fastest way to get your data from SharePoint, problem is that building a dynamic CAML query can be rather tedious, hard to read and error prone.

So the solution:

First you should get the Dynamic library from ScottGu's Blog.

Now to get your list items you can run your CAML to do as much as possible, ie return the list items sorted by modified date, filtered by Content Type and maybe bring back a specific number of items (though in this example I cannot specify the RowLimit on my SPQuery as I need the full list to further filter from).

Then if you have further parameters that you need to filter on, ie multiple drop down lists that a user may or may not select, then you can employee LINQ as below:

            
//Get your list collection ordered and filtered as much as you can using CAML
SPListItemCollection listCollection = web.Lists[listName].GetItems(query);

//check that user is filtering by both industry and country
if (IndustryItem != null && countryItem != null)
{

listItems = listCollection.Cast<SPListItem>()
.Where(p => (p.GetFormattedValue("Country").Contains(countryItem.Title) && p.GetFormattedValue("Industry").Contains(industryItem.Title)))
.Take(5);
}


Of course you would require a few more else if blocks depending on the number of options to filter on but as you can see alot easier to achieve it this way then using just CAML.

Thursday, May 14, 2009

Custom Buttons for MOSS HTML Editor

It is recommended that you use the Telerik RadEditor Lite for MOSS which is free if you have a valid MOSS 2007 license, but if you end up using the MOSS HTML editor you can enrich it by adding custom buttons.

Below I will create a button that will uppercase all selected text:

We start by creating a JavaScript file RTEToUppercase.js in [..]\12\TEMPLATE\LAYOUTS\1033 folder.
If you are using WSP Builder in your project then you will just create the folder path above starting from the Template folder and add the JavaScript file their.

We need to add the first function into the RTEToUppercase.js as follows:

//RTE2_RegisterToolbarButton("ID of button", "IconUrl", "Toolbar Text", "Tooltip", "ClickCallback function name", "ResetStateCallback function name")

RTE2_RegisterToolbarButton("ToUpperCase",
"",
"UPPERCASE",
"Change selected text to uppercase",
ToUpperCaseButtonOnClick,
ToUpperCaseButtonOnResetState,
new Array());

//Then we need to create the ClickCallback function which does all the work when the button is clicked

function ToUpperCaseButtonOnClick(strBaseElementID, args){
var docEditor = RTE_GetEditorDocument(strBaseElementID);
if (docEditor == null) { return; }
//gets the selected html/text
var selectedRange = docEditor.selection.createRange();
//uppercase the selected text
var strUpperCased = selectedRange.htmlText.toUpperCase();
//paste it back to editor
selectedRange.pasteHTML(strUpperCased);
//restore selection
RTE_RestoreSelection(strBaseElementID);

return true;
}

//Finally we create the ResetStateCallback function to enable or disable the button depending on whether or not text has been selected

function ToUpperCaseButtonOnResetState(strBaseElementID, args){
var docEditor = RTE_GetEditorDocument(strBaseElementID);
if (docEditor == null) { return; }
//restore selection
RTE_RestoreSelection(strBaseElementID);
//check if we have selected text
if (docEditor.selection.createRange().text.length != 0){
RTE_TB_SetEnabledFromCondition(strBaseElementID, true, "ToUpperCase");
} else {
RTE_TB_SetEnabledFromCondition(strBaseElementID, true, "ToUpperCase"); }
return true;
}

Save the file

Then we need to edit the following file Site Actions -> Site Settings -> Modify All Site Settings -> Master pages and page layouts -> Editing Menu -> RTE2ToolbarExtension.xml

Add the foillowing to the XML file

<?xml version="1.0" encoding="utf-8" ?>
<RTE2ToolbarExtensions>
<RTE2ToolbarExtraButton id="ToUpperCase" src="RTEToUppercase.js"/>
</RTE2ToolbarExtensions>

Publish and away you go.

But instead of editing manually the best way is to add a Feature that will deploy the RTE2ToolbarExtension.xml file.

Unfortunately you cannot use provisioning technique to deploy this file but you can use Feature recievers as shown in the following blog http://msdn.microsoft.com/en-us/library/cc843966.aspx under "Deploying Page Editing Toolbar Customizations" section.

Once you have installed and Activated your feature you should have your UPPERCASE button appear when you edit using the Rich HTML Editor as shown below.











Thursday, May 7, 2009

JS library that makes IE5-6 render like IE7! No more IE6 hacks!

I was looking at another frustrating CSS cross browser issue where I wanted to change a style on the first list item using :first-child but of course IE6 did not support this. So I was looking at examples that set a class on the first item manually or by using jQuery but then happened apon the following:

Dean Edwards has created a JS library that makes IE5-6 render like IE7, so it fixes issues like png transparency, adds the ability to use :first-child and more.
He also has a different release which gives IE5-7 advanced CSS features missing from IE7 like :contains(), :nth-last-child() and more

His site
http://dean.edwards.name/weblog/2008/01/ie7-2/

see demos here
http://ie7-js.googlecode.com/svn/test/index.html

Thursday, April 2, 2009

SharePoint Custom Field Controls

Creating custom field controls for Sharepoint at first looks abit hairy but like anything once you have created a couple they are not so hairy after all.

There are plenty of posts on field controls and Microsoft have some good stuff up aswell, but what I found very useful and time saving was finding this post Creating a Rendering Template that supports Edit and Display modes which describes many findings that are not well documented and would cause anyone a head ache trying to find this out by themselves.

So given all the good posts already up about the various overrides and so on in field controls I will just paste a field control I created for displaying videos as an example, you can see it in commerical action here ( http://nzte.govt.nz/features-commentary/Success-stories/Pages/Growing-with-NZTE-part-one.aspx ).

FeaturedVideo.ascx


<%@ Control Language="C#" %>
<%@ Register TagPrefix="PublishingWebControls" Namespace="Microsoft.SharePoint.Publishing.WebControls"
Assembly="Microsoft.SharePoint.Publishing, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls"
Assembly="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<sharepoint:renderingtemplate id="FeaturedVideoControl" runat="server">
<Template>
<PublishingWebControls:EditModePanel ID="editModePanel" SuppressTag="true" runat="server">
<asp:Label id="messageLabel" CssClass="message" runat="server" />
<div>Video File</div>
<PublishingWebControls:AssetUrlSelector ID="urlVideoSelector" AllowExternalUrls="false" IsUrlRequired="true" ValidateUrl="false" runat="server" />
<div>Video Transcript Page</div>
<PublishingWebControls:AssetUrlSelector ID="urlVideoTranscriptPageSelector" IsUrlRequired="true" ValidateUrl="false" AllowExternalUrls="false" runat="server" /><br />
</PublishingWebControls:EditModePanel>
<PublishingWebControls:EditModePanel ID="viewModePanel" PageDisplayMode="Display" SuppressTag="true" runat="server">
<!-- Video Player -->
<div class="video-holder">
<div class="video-pod">
<div class="content">
<img id="previewImg" runat="server" height="85" width="233" />
<p class="video-show"><a id="videoAnchor" runat="server" title="Click here to open the video">Open Video Player</a></p>
</div>
</div>
<p class="video-abstract"><asp:Literal ID="videoAbstractLiteral" runat="server" /></p>
<p class="video-transcript"><a id="videoTranscriptAnchor" runat="server" title="Click here to view video transcript">Click here to view video transcript</a></p>
</div>
</PublishingWebControls:EditModePanel>
</Template>
</sharepoint:renderingtemplate>

FeaturedVideoControl.cs


namespace NZTE.SharePoint.FieldControls
{
public class FeaturedVideoControl : BaseFieldControl
{
#region Member Variables

protected EditModePanel editModePanel;
protected EditModePanel viewModePanel;
protected AssetUrlSelector urlVideoSelector;
protected AssetUrlSelector urlVideoTranscriptPageSelector;
protected HtmlImage previewImg;
protected HtmlAnchor videoAnchor;
protected HtmlAnchor videoTranscriptAnchor;
protected Literal videoAbstractLiteral;
protected Label messageLabel;

#endregion

#region Constants

private const string m_editModePanel = "editModePanel";
private const string m_viewModePanel = "viewModePanel";
private const string m_urlVideoSelector = "urlVideoSelector";
private const string m_urlVideoTranscriptPageSelector = "urlVideoTranscriptPageSelector";
private const string m_previewImg = "previewImg";
private const string m_videoAnchor = "videoAnchor";
private const string m_videoAbstractLiteral = "videoAbstractLiteral";
private const string m_videoTranscriptAnchor = "videoTranscriptAnchor";
private const string m_messageLabel = "messageLabel";
private const string m_controlTemplateName = "FeaturedVideoControl";
private FeaturedVideoFieldValue _value = new FeaturedVideoFieldValue();

#endregion

#region Property Overrides

///
/// Name of rendering template
///

protected override string DefaultTemplateName
{
get
{
return m_controlTemplateName;
}
}

///
/// Name of display rendering template
///

public override string DisplayTemplateName
{
get
{
return m_controlTemplateName;
}
}

///
/// Current value for control
///

public override object Value
{
get
{
EnsureChildControls();

_value.VideoUrl = urlVideoSelector.AssetUrl.ToString();
_value.TranscriptUrl = urlVideoTranscriptPageSelector.AssetUrl.ToString();

//Return the set value
return _value;
}

set
{
EnsureChildControls();

_value = new FeaturedVideoFieldValue(value.ToString());

urlVideoSelector.AssetUrl = _value.VideoUrl;
urlVideoTranscriptPageSelector.AssetUrl = _value.TranscriptUrl;
}
}

#endregion

#region Method Overrides

protected override void OnInit(EventArgs e)
{
CanCacheRenderedFieldValue = Constants.EnableBaseFieldControlCache;
base.OnInit(e);
}

///
/// Creates the child controls.
///

protected override void CreateChildControls()
{
if (this.Field != null && this.ControlMode == SPControlMode.Display)
{
base.CreateChildControls();
FindDisplayControls();
}

if (this.Field != null && this.ControlMode == SPControlMode.Edit)
{
base.CreateChildControls();
FindEditControls();
}
}

///
/// Validates this instance.
///

public override void Validate()
{
FeaturedVideoFieldValue field = _value;

<--validate each field and if any fail then set the following-->
if (field fails validation)
{
this.ErrorMessage = "Your error message";
IsValid = false;
return;
}
}

#endregion

#region Private Methods

///
/// Finds the display controls.
///

private void FindDisplayControls()
{
viewModePanel = (EditModePanel)Utilities.FindAndValidateControl(TemplateContainer, m_viewModePanel);
viewModePanel.PreRender += new EventHandler(viewModePanel_PreRender);
previewImg = (HtmlImage)Utilities.FindAndValidateControl(viewModePanel, m_previewImg);
videoAnchor = (HtmlAnchor)Utilities.FindAndValidateControl(viewModePanel, m_videoAnchor);
videoTranscriptAnchor = (HtmlAnchor)Utilities.FindAndValidateControl(viewModePanel, m_videoTranscriptAnchor);
videoAbstractLiteral = (Literal)Utilities.FindAndValidateControl(viewModePanel, m_videoAbstractLiteral);
}

///
/// Finds the edit controls.
///

private void FindEditControls()
{
editModePanel = (EditModePanel)Utilities.FindAndValidateControl(TemplateContainer, m_editModePanel);
messageLabel = (Label)Utilities.FindAndValidateControl(editModePanel, m_messageLabel);
urlVideoSelector = (AssetUrlSelector)Utilities.FindAndValidateControl(editModePanel, m_urlVideoSelector);
urlVideoTranscriptPageSelector = (AssetUrlSelector)Utilities.FindAndValidateControl(editModePanel, m_urlVideoTranscriptPageSelector);
}

///
/// Handles the PreRender event of the viewModePanel control.
///

/// The source of the event.
/// The instance containing the event data.
protected void viewModePanel_PreRender(object sender, EventArgs e)
{
EnsureChildControls();

if (ItemFieldValue != null)
{
_value = new FeaturedVideoFieldValue(ItemFieldValue.ToString());

if (!String.IsNullOrEmpty(_value.VideoUrl))
{
BindVideoControls(_value.VideoUrl);
BindTranscript(_value.TranscriptUrl);
}
else
viewModePanel.Visible = false;
}
else
viewModePanel.Visible = false;
}

#endregion
}
}

FeaturedVideoField.cs


namespace NZTE.SharePoint.FieldControls
{
public class FeaturedVideoField : SPFieldMultiColumn
{
///
/// Initializes a new instance of the class.
///

/// The fields.
/// Name of the field.
public FeaturedVideoField(SPFieldCollection fields, string fieldName)
: base(fields, fieldName)
{
}

///
/// Initializes a new instance of the class.
///

/// The fields.
/// Name of the type.
/// The display name.
public FeaturedVideoField(SPFieldCollection fields, string typeName, string displayName)
: base(fields, typeName, displayName)
{
}

///
/// Gets the field rendering control.
///

/// The field rendering control.
public override BaseFieldControl FieldRenderingControl
{
[SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
get
{
BaseFieldControl fieldControl = new FeaturedVideoControl();
fieldControl.FieldName = this.InternalName;

return fieldControl;
}
}
}
}

FeaturedVideoFieldValue.cs


namespace NZTE.SharePoint.FieldControls
{
public class FeaturedVideoFieldValue : SPFieldMultiColumnValue
{
private const int NUM_FIELDS = 2;

public FeaturedVideoFieldValue()
: base(NUM_FIELDS) { }

public FeaturedVideoFieldValue(string value)
: base(value) { }

public string VideoUrl
{
get { return this[0]; }
set { this[0] = value; }
}

public string TranscriptUrl
{
get { return this[1]; }
set { this[1] = value; }
}
}
}


FieldControls.xml




FeaturedVideoField
Featured Video Field
Featured Video field
Note
NZTE.SharePoint.FieldControls.FeaturedVideoField, NZTE.SharePoint, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8ed5fb6d12664acd
TRUE