SharePoint ListFieldIterator Include Fields


The standard SharePoint ListFieldIterator used in the templates of display, edit and new forms has a property called ExcludeFields. This property allows you to set fields you do not want to render. When you are doing some custom form with for example jquery tabs or an accordion, you might want to use this property to control the fields that are rendered in your elements. When you have a lot of fields this might become unmanagable, because you would have to practically fill in all fields multiple times.

I created a modified version of the standard ListFieldIterator where you can just set the fields that you want. The modification includes hiding the exclude fields property and adding a new property called IncludeFields. The logic is quite simple; you specify the fields you want to see and the code fills in the excludefields of the base webcontrol so you dont have to.

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint;

namespace Sample.Web.WebControls
{
    public class IncludeFieldsListFieldIterator : ListFieldIterator
    {
        private string _includeFields;

        public string IncludeFields
        {
            get
            {
                return _includeFields;
            }
            set
            {
                _includeFields = value; 

                string[] includeFieldNames = new string[] { };

                if (!string.IsNullOrEmpty(_includeFields))
                {
                    includeFieldNames = _includeFields.Split(new string[] { ";#" }, StringSplitOptions.RemoveEmptyEntries);
                }

                List<string> excludeFieldNames = new List<string>();

                foreach (SPField field in this.Fields)
                {
                    string staticName = field.StaticName;

                    if (!includeFieldNames.Contains(staticName, StringComparer.OrdinalIgnoreCase))
                    {
                        excludeFieldNames.Add(staticName);
                    }
                }

                base.ExcludeFields = string.Join(";#", excludeFieldNames.ToArray());
            }
        }

        private new string ExcludeFields
        {
            get
            {
                return base.ExcludeFields;
            }
            set
            {
                base.ExcludeFields = value;
            }
        }
    }


The control can be used in your templates (put in the controltemplates folder for example) as following; add the assembly and register the tagprefix;

<%@ Assembly Name="Sample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=publickeytokenhere" %>
<%@ Register TagPrefix="sample" Assembly="Sample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=publickeytokenhere" Namespace="Sample.Web.WebControls" %>


Then use the control inside a formtable like this, you can encapsulate it in any type of html element for creating the user interface using jquery.ui for example.

<div id="accordion" width="100%">
  <h3>Panel 1</h3>
  <div>
    <TABLE class="ms-formtable" style="margin-top: 8px;" border="0" cellpadding="0" cellspacing="0" width="100%">
      <sample:IncludeFieldsListFieldIterator runat="server" IncludeFields="field1;#field2" />
    </TABLE>
  </div>

  <h3>Panel 2</h3>
  <div>
    <TABLE class="ms-formtable" style="margin-top: 8px;" border="0" cellpadding="0" cellspacing="0" width="100%">
      <sample:IncludeFieldsListFieldIterator runat="server" IncludeFields="field3;#field4" />
    </TABLE>
  </div>
</div>


Tags: , , ,

author: Ruud | posted @ Friday, April 01, 2011 10:22 AM | Feedback (0)

Hiding fields in the Document Information Panel


Sometimes you might want to hide certain fields from the Document Information Panel (DIP), but you do not want to customize the panel because you would need visio on the clients. You can simply hide fields by setting the "ShowInFileDlg" attribute in the field element or fieldref element to "FALSE". 

The SharePoint documentation is not very clear about this, but it is supposed to work . You can also do this by code, there is no property for it but you will have to update the field schema xml.

More info
http://msdn.microsoft.com/en-us/library/aa979575.aspx


Tags: , ,

author: Ruud | posted @ Wednesday, March 16, 2011 11:02 AM | Feedback (1)

FileNotFoundException: The site with the id {GUID} could not be found


This error popped up on an environment where we deploy stuff on and off for testing. Turns out the blob cache was invalid, resetting the object cache for the sitecollection in the site settings fixed the problem.

See more info in this post: http://blogs.msdn.com/b/joshuag/archive/2008/05/22/filenotfoundexception-the-site-with-the-id-guid-could-not-be-found.aspx


Tags: ,

author: Ruud | posted @ Tuesday, March 15, 2011 10:18 AM

Improve performance using the PortalSiteMapProvider


When dealing with large environments, getting data using the SharePoint object model can be slow. After some tests I found a good alternative to fetch subwebs and their metadata stored in the property bag by using the PortalSiteMapProvider located in the Microsoft.SharePoint.Publishing.Navigation namespace.

The following code sample will recursively fetch all your subwebs including a custom 'status' metadata field in the property bag using the method "GetProjects".

public class Project
{
   public string Title { get; set; }
   public Guid WebId { get; set; }
   public string Url { get; set; }
   public string ServerRelativeUrl { get; set; }
   public string Status { get; set; }
}

public List<Project> GetProjects()
{
  
List<Project> projects = new List<Project>();

   PortalSiteMapProvider portalSiteMap = PortalSiteMapProvider.WebSiteMapProvider; 
   portalSiteMap.IncludePages = PortalSiteMapProvider.IncludeOption.Never; 
   portalSiteMap.IncludeHeadings = false; 
   portalSiteMap.IncludeAuthoredLinks = false; 
   portalSiteMap.IncludeSubSites = PortalSiteMapProvider.IncludeOption.Always;
   portalSiteMap.DynamicChildLimit = 0;
   portalSiteMap.FlattenHeadings = true;

   PortalSiteMapNode rootNode = portalSiteMap.TryGetRootNode;

   if (rootNode != null && rootNode.Type != NodeTypes.None)
   { 
      GetProjectsByPortalSiteMapProvider(rootNode, ref projects);
   }

   return projects;
}

private void GetProjectsByPortalSiteMapProvider(PortalSiteMapNode fromNode, ref List<Project> projects)
{
   SiteMapNodeCollection nodes = (fromNode.Provider as PortalSiteMapProvider).GetChildNodes(fromNode as PortalSiteMapNode, NodeTypes.Area, NodeTypes.Area);

   foreach (SiteMapNode node in nodes)
   { 
      PortalWebSiteMapNode portalNode = node as PortalWebSiteMapNode;

      Project project = new Project(); 
      project.WebId = portalNode.WebId; 
      project.Title = portalNode.Title; 
      project.ServerRelativeUrl = portalNode.ServerRelativeUrl; 
      project.Url = portalNode.Url; 
      
      object propertyObject; 
      if (portalNode.TryGetProperty("Status", out propertyObject)) 
      { 
         project.Status = propertyObject as string; 
      } 

      projects.Add(project); 

      GetProjectsByPortalSiteMapProvider(portalNode, ref projects);
   }
}


The PortalSiteMapProvider proves to be very effective in this case; the first hit can be slower than the object model, but the next requests will be very fast. It will return a list of all your project subwebs in a matter of milliseconds, much faster than the SPSite.AllWebs or SPWeb.GetSubwebsForCurrentUser which might take even hundreds of milliseconds or even seconds when there are a lot of subwebs. 

This example shows a collection that does not change frequently and the data is queried several times so the provider can be a good choice in this scenario.

More info
Using the PortalSiteMapProvider
http://msdn.microsoft.com/en-us/library/ff647530.aspx  

Increased performance for MOSS apps using the PortalSiteMapProvider
http://blogs.msdn.com/b/ecm/archive/2007/05/23/increased-performance-for-moss-apps-using-the-portalsitemapprovider.aspx


Tags: ,

author: Ruud | posted @ Monday, March 07, 2011 11:39 PM | Feedback (0)

Extend WebParts using IScriptControl


Sometimes we will have to build WebParts or controls that contain JavaSript (ECMAScript). Most of the time this is done by using an external script file, a string (StringBuilder) or an embedded resource file that we write out.

Since the inclusion of System.Web.Extensions in the framework and the fact that SharePoint 2010 by default contains a ScriptManager on the masterpage we can now also use an interface called IScriptControl to create server controls that include script control functionality. Benefits of this method include:

  • The JavaScript is readable and manageble.
  • The script will be encapsulated for each control.
  • Server and client code are tied in close together.

The following steps will guide you to build your own WebPart that is a ScriptControl. It can be a little tricky at first, but when done, the process can be easily copied for new instances. A sample project is included below.

Step 1: Create a WebPart

Start out in an "Empty SharePoint" project and add new webpart in your solution using "Add > New item > WebPart". For this sample we name it: "AlertWebPart".

Step 2: Add the JavaScript file

Add a javascript file (JScript file in VS) to the WebPart folder, using the same name as the class. Set the "Build Action" property of the file to "Embedded Resource" to include the file in the dll.

Fill the contents of the file with the skeleton below. Be sure to use your namespace and classname (note that Visual Studio 2010 will create a seperate namespace for your WebPart classes).

Type.registerNamespace("ScriptControlWebPartDemo.AlertWebPart");

ScriptControlWebPartDemo.AlertWebPart.AlertWebPart = function (element)
{
 
ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.initializeBase(this, [element]);
}

ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.prototype =
{
  initialize: function ()
  { 
    ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.callBaseMethod(this, "initialize");
  },

  dispose: function ()
  { 
    ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.callBaseMethod(this, "dispose");
  }
}

ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.registerClass("ScriptControlWebPartDemo.AlertWebPart.AlertWebPart", Sys.UI.Control);
 

Step 3: Add WebResource

The next step is to add the embedded JavaScript file as a WebResource. Do this by adding the following line to your WebPart class, preferred location is under the usings and above the namespace tag.

[assembly: System.Web.UI.WebResource("ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.js", "text/javascript")]

Step 4: Implement IScriptControl and register it with the ScriptManager

The next thing to is to implement the interface IScriptControl. Add the following code to the OnPreRender method to register the control with the ScriptManager on the page and implement the 2 methods from the interface:

  • GetScriptDescriptors
    Gets a collection of script descriptors that represent ECMAScript (JavaScript) client components.
  • GetScriptReferences
    Gets a collection of ScriptReference objects that define script resources that the control requires.

public class MyWebPart: WebPart, IScriptControl

..

protected override void OnPreRender(EventArgs e)
{
    // Register the ScriptControl with the ScriptManager.
    if (!this.DesignMode)
    {
      ScriptManager sm = ScriptManager.GetCurrent(Page);
      
      if (sm == null)
        throw new HttpException("A ScriptManager control must exist on the current page.");

      sm.RegisterScriptControl(this);
    }
    base.OnPreRender(e);
}

protected override void Render(HtmlTextWriter writer) 
{
    // Register the ScriptDescriptors, but not in design mode. 
    if (!this.DesignMode) 
      ScriptManager.GetCurrent(Page).RegisterScriptDescriptors(this);

    base.Render(writer);
}

IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()
{
  ScriptReference reference = new ScriptReference();

  if (Page != null)
    reference.Path = Page.ClientScript.GetWebResourceUrl(this.GetType(), "ScriptControlDemo.AlertWebPart.AlertWebPart.js");

  return new ScriptReference[] { reference };
}

IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
{
  this.EnsureChildControls(); 

  ScriptControlDescriptor descriptor = new ScriptControlDescriptor("ScriptControlWebPartDemo.AlertWebPart.AlertWebPart", ClientID); 

  // Descriptor code goes here...

  return new ScriptDescriptor[] { descriptor };
}
 

Step 5: Implement the server code of the script functionality

We now have a webpart that supports script, but there is no functionality. Let's add a button and a WebPart property "Message" which we will show in an JavaScript alert when we click on the button.

protected Button alertButton; 

[Personalizable(PersonalizationScope.Shared),
WebBrowsable(true),
WebDisplayName("Message"),
WebDescription("Gets or sets the message."),
Category("Custom")]
public string Message
{
  get;
  set;
}

protected override void CreateChildControls()
{
  alertButton = new Button();
  alertButton.ID = "messageButton";
  alertButton.Text = "Click me";
  Controls.Add(alertButton);
}

Then we need to push the message property to the script, we also want to push the client id of the button so we can find the control in de DOM for later usage. We do this in the GetScriptDescriptors method.

IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()
{
  this.EnsureChildControls();
  ScriptControlDescriptor descriptor= new ScriptControlDescriptor("ScriptControlWebPartDemo.AlertWebPart.AlertWebPart", ClientID); 
  descriptor.AddProperty("ButtonID", alertButton.ClientID); 
  descriptor.AddProperty("Message", Message);

  return new ScriptDescriptor[] { descriptor};
}

Step 6: Implement the client script code of the script functionality 

The last step is to implement the client script. First we add local variables to hold the property values. Second we add a get and set method for the properties. We add our client function we want to execute and finally we tie in the click event to the button we created earlier.

Type.registerNamespace("ScriptControlWebPartDemo.AlertWebPart");

ScriptControlWebPartDemo.AlertWebPart.AlertWebPart = function (element)

  ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.initializeBase(this, [element]);
 
  // Define the local variables.
  this._message = null;
  this._buttonID = null;
}

ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.prototype =
{
  initialize: function ()
  { 
    ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.callBaseMethod(this, "initialize"); 

    // Get the button.
    var button = $get(this.get_ButtonID(), this.get_element());

    // If the button was found, add a handler to the click event.
    if (button)
    {
      $addHandler(button, "click", Function.createDelegate(this, this._buttonClick));
    }
  },

  dispose: function ()
  {
    ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.callBaseMethod(this, "dispose");
 
},

  _buttonClick: function (evt)
  { 
    // Stop further events.
    evt.preventDefault();
    evt.stopPropagation();
  
    // Alert the message we have set in the WebPart property.
    alert(this.get_Message());
  },

  get_Message: function ()
  {
    return this._message;
  },

  set_Message: function (value)
  {
    if (value != this._message)
    {
      this._message = value; this.raisePropertyChanged("Message");
    }
  },

  get_ButtonID: function ()
  {
    return this._buttonID;
  },

  set_ButtonID: function (value)
  {
    if (value != this._buttonID)
    {
      this._buttonID = value; this.raisePropertyChanged("ButtonClientID");
    }
  }
}

ScriptControlWebPartDemo.AlertWebPart.AlertWebPart.registerClass("ScriptControlWebPartDemo.AlertWebPart.AlertWebPart", Sys.UI.Control);

 

Run the sample

Build and deploy the solution, add the WebPart to a page and set the custom "Message" property. Save the page and click the button of the WebPart. The alert should now show the message you have just entered.

From here

To start out, you can download the sample code here. You might want to implement stuff in a base class and use jQuery in the client script part. The client script code can be used to easily implement calls to webservices, use the JavaScript client object model, create animations, content rotators, tabs and so on.


Tags: , , ,

author: Ruud | posted @ Thursday, November 04, 2010 11:00 PM | Feedback (0)

Helpful SharePoint JavaScript functions


The init.js and core.js JavaScript files in the layouts directory of SharePoint 2007 contain a lot of helpful JavaScript functions. Here is a brief list of some of the most helpful classes, functions and variables available.

If you are looking for SharePoint 2010, check out the extensive ECMAScript Class Library reference on MSDN: http://msdn.microsoft.com/en-us/library/ee538253.aspx, the SP.Utilities namespace contains loads of helpful utility functions.

function STSScriptEncode(str)

This function can be used to encode javascript variable for usage in scripts.

Location
init.js

Parameters
str
   The string that has to be encoded.

Return value
The encoded string that can be used in scripts.

Example
var strAction = "STSNavigate('" + STSScriptEncode(currentItemUrl) + "')";

function STSScriptEncodeWithQuote(str)

This function can be used to encode and quote javascript variable for usage in scripts.

Location
init.js

Parameters
str
   The string that has to be encoded.

Return value
The encoded string that can be used in scripts, that is enclosed in quotes (").

Example
var strAction = 'STSNavigate(' + STSScriptEncodeWithQuote(currentItemUrl) + ')';

function STSHtmlEncode(str)

This function can be used to encode a string to html.

Location
init.js

Parameters
str
   The string that has to be encoded.

Return value
The string encoded to html.

Example
var html = STSHtmlEncode('htmlString');

function STSNavigate(url)

This function can be used to navigate to a given url.

Location
init.js

Parameters
url
   A string that contains the url to navigate to.

Example
STSNavigate('http://www.microsoft.com');

function STSPageUrlValidation(url)

This function can be used to validate an url whether it starts with "http" or with a slash '/'. The function raises an alert when the url is not valid.

Location
init.js

Parameters
url
   A string that contains the url to check

Return value
If the url is valid it will return the url that has been given as the parameter, if it is not valid it wil return an empty string.

Example
var url = PageUrlValidation('http://www.microsoft.com');

function GetUrlKeyValue(kenName, bNoDecode, url)

This function can be used to get a querystring parameter.

Location
init.js

Parameters
keyName
   A string that contains the name of the parameter.

bNoDecode
   A boolean that states whether the value has will not be encoded, this parameter is optional, default is false.

url
   A string that contains the url to fetch the querystring parameters from, this is optional, the window.location.href will be used if the parameter is null.

Return value
The value of the parameter, encoded if bNoDecode was false.

Example
var action = GetUrlKeyValue('action');

function GetSource(defaultSource)

This function can be used to get the source parameter from the querystring.

Location
init.js

Parameters
defaultSource
   A string that contains the default source, this parameter is optional.

Return value
If the source querystring parameter exists it will return the value of that parameter, otherwise if the defaultSource parameter was given it will return that. In any oter case it will return window.location.href.

Example
var source = GetSource();

function LoginAsAnother(url, bUseSource)

This function can be to change the current user.

Location
init.js

Parameters
url
   A string that contains url to go to after the login.

bUseSource
   A boolean that indicates that the source will be added to the url, otherwise the source will be the window.location.href. This parameter is optional, default is false.

Example
<a href="#" onclick="javascript:LoginAsAnother('\u002f_layouts\u002fAccessDenied.aspx?loginasanotheruser=true', 0)">Log on as a different user</a>

function GoToPage(url)

This function can be used to navigate to an url, adding a source querystring parameter.

Location
init.js

Parameters
url
   A string that contains url to navigate to.

Example
<a href="#" onclick="javascript:GoToPage('/_layouts/settings.aspx')">Settings</a>

function TrimSpaces(str)

This function can be to trim spaces on a string.

Location
init.js

Parameters
str
   A string that will be trimmed.

Return value
The trimmed string.

Example
var trimmed = TrimSpaces(" string with spaces "); // Returns "string with spaces".

function TrimWhiteSpaces(str)

This function can be to trim whitespaces on a string, that is spaces, tabs and linebreaks (\t \n \r \f).

Location
init.js

Parameters
str
   A string that will be trimmed.

Return value
The trimmed string.

Example
var trimmed = TrimWhiteSpaces("\t\tstring with spaces\n");  // Returns "string with spaces".

function escapeProperly(str)

This function takes a string and returns a URL-encoded string.

Location
init.js

Parameters
str
   A string that will be encoded.

Return value
The encoded string.

Example
var urlEncodedValue = escapeProperly("My Value"); // Returns "My%20Value".

function unescapeProperly(str)

This function takes a URL-encoded string and returns an string.

Location
init.js

Parameters
str
   A string that will be decoded.

Return value
The string.

Example
var urlDecodedValue = unescapeProperly("My%20Value"); // Returns "My Value".

class JSRequest

The JSRequest object provides parsing of the querystring, you can easily use this object to get querystring variables, filenames and pathnames. 

Location
init.js

Example
JSRequest.EnsureSetup();

// The current url in this example is http://localhost/pages/default.aspx?debug=true
var debug = JSRequest.QueryString["debug"]; // Returns 'true'.
var fileName = JSRequest.FileName; // Returns 'default.aspx'.
var pathName = JSRequest.PathName; // Returns '/pages/default.aspx'.

array _spBodyOnLoadFunctionNames

This array allows you to register additional JavaScript methods that should run in the body onload event.

Location
core.js

Example
_spBodyOnLoadFunctionNames.push('functionName');

You can also pass arguments, for example an id. The next example shows an argument of type string.

_spBodyOnLoadFunctionNames.push('functionName("functionArgument")');

variable L_Menu_BaseUrl

This variable contains the base URL of the current site or subsite. 

Location
Inline

Example
document.location = L_Menu_BaseUrl + 'Lists/Tasks/AllItems.aspx';

variable L_Menu_LCID

This variable contains the LCID setting of the current site.

Location
Inline

variable L_Menu_SiteTheme

This variable contains the theme name of the current site.

Location
Inline

variable _spUserId

This variable contains the id of the current user.

Location
Inline

Deferred loading

Be careful using the out of the box scripts from the core.js when it is loaded deferred. In that case, add your scripts to the JQuery on document.ready or via _spBodyOnLoadFunctionNames to be sure that the functions are present.
 

Tags: , ,

author: Ruud | posted @ Tuesday, August 03, 2010 9:31 PM | Feedback (4)

SharePoint Publishing HTML Field and Array prototypes


As you already might have encountered yourself, custom JavaScript and SharePoint JavaScript can sometimes bite each other. In a recent scenario, a simple Array prototype caused the Publising HTML field to crash, this is the field that is used as page content on publising site pages. 

A loop on a properties Array in the AssetPickers.js file causes the malfunction. It generates a "Object doesn't support this property or method" error when you click the "Edit Content" or "Click here to add new content" link which supposed to show the editing toolbar.

This is an example of a protoype adding an indexOf function to arrays, as implemented by mozilla, which is not available in Internet Explorer.

//This prototype is provided by the Mozilla foundation and
//is distributed under the MIT license.
//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license

if (!Array.prototype.indexOf)
{
   Array.prototype.indexOf = function(elt /*, from*/)
   {
       var len = this.length;
       var from = Number(arguments[1]) || 0;
       from = (from < 0) ? Math.ceil(from) : Math.floor(from);
       if (from < 0) from += len;

       for (; from < len; from++)
       {
          if (from in this && this[from] === elt)
             return from;
       }
       return -1;
   };
}


This is the line in AssetPicker.js that causes the malfunction, it will see the prototyped function as an item in the array and tries to execute a split on it that will throw the exception because it is not a string.

function AP_PopulateFromSerializedObjectQString( objectToPopulate, queryStringSegment )
{
   var currentWorkingQString=queryStringSegment
   var indexOfQStringStart=queryStringSegment.indexOf("?");
   if( -1 !=indexOfQStringStart )
   {
      currentWorkingQString=queryStringSegment.substr(indexOfQStringStart);
   }
   var propArray=currentWorkingQString.split("&");
   for( var i in propArray )
   {
      var nameValuePair=propArray[i].split("=");
      if(nameValuePair.length==2)
      {
         objectToPopulate[nameValuePair[0]]=decodeURIComponent(nameValuePair[1]);
      }
   }
}

To fix this you could change the AssetPickers.js file or you should not add prototypes to an Array on publishing pages and
use a custom function or a jQuery utility function to get the index, the latter is preferred.

Fix for function in AssetPickers.js

function AP_PopulateFromSerializedObjectQString( objectToPopulate, queryStringSegment )
{
   var currentWorkingQString=queryStringSegment
   var indexOfQStringStart=queryStringSegment.indexOf("?");
   if( -1 !=indexOfQStringStart )
   {
      currentWorkingQString=queryStringSegment.substr(indexOfQStringStart);
   }
   var propArray=currentWorkingQString.split("&");
   for( var i in propArray )
   {
      if(propArray.hasOwnProperty(i))
      {

         var nameValuePair=propArray[i].split("=");

         if(nameValuePair.length==2)
         {
            objectToPopulate[nameValuePair[0]]=decodeURIComponent(nameValuePair[1]);
         }
      }
   }
}


Alternative jQuery function

jQuery.inArray("item", myArray);

 

Tags: , ,

author: Ruud | posted @ Sunday, July 25, 2010 10:11 PM | Feedback (0)

Please wait while scripts are loaded...


This oldie popped up recently while I was creating some script using the AjaxControlToolkit in SharePoint. In some cases, SharePoint JavaScript will sometimes put the message "Please wait while scripts are loaded..." in the status bar and stops executing scripts. I knew there was a trick to work around this so I could debug the script. After some googling I found this post again: http://www.nerdyhearn.com/blog.php?id=78.

If you put the following statement above your script, the exception that causes javascript to crash will actually be thrown so you can fire up your debugger.

g_pageLoadComplete = true;

Be sure to remove it afterwards.

Tags: , ,

author: Ruud | posted @ Friday, July 02, 2010 12:00 AM | Feedback (0)

TFS Merge action found no changes


While merging some branches with Team Foundation Server (TFS) I encountered a strange error: some files in a branch could not be merged back to the source because TFS did not see the changes in the files located in that branch.

The set up was a base project that was branched to two different other projects. The goal was to merge changes in both branches to the base and then the final version back to each branch.

I tried several things:

  • Get specific version to enforce the latest files.
  • Roll back the changes and merge again.
  • Create a new workspace.

All these things would not help, so I googled around a bit and found out that there is an action called "Force Merge" and "Baseless merge". The latter action is mainly used when you want to merge code when there is no relationship between the branches.

Perform a Rollback
I also checked if there was a specific action for rolling back a changeset. I found out that there is a rollback action in the Team Foundation Power Tools, that can be downloaded here. This action is not available in Visual Studio.

When you have downloaded and installed the Power Tools you have the ability to use a command line program called "tfpt.exe". To start a rollback, use the command:

  • tfpt rollback
    This opens up a changeset picker to roll back to.
  • tfpt rollback /changeset:<changesetno>
    Rolls back to the specified changeset.

Be sure to execute the command in a folder that is mapped to source control, otherwise it will give the error "Unable to determine the workspace". After the command you can check in the changes and you have restored the code to the specified changeset.

More info on Team Foundation Power Tools:
http://blogs.msdn.com/b/buckh/archive/2005/11/16/493401.aspx

Perform a Forced Merge
A forced merge can be done using the "tf.exe" command line tool. This executable resides in the IDE folder, for Visual Studio 2008 this is by default "C:\%programfiles%\Microsoft Visual Studio 9.0\Common7\IDE\tf.exe". Then issue the command for a baseless merge:

  • Tf merge /force <<source path>> <<target path>> /recursive

All the files will be merged and any conflicts are shown and handled using the standard Resolve Conflicts dialog box.

Perform a Baseless Merge
A baseless merge can be done using the "tf.exe", found at the location described above.

  • Tf merge /baseless <<source path>> <<target path>> /recursive

All the files will be merged and any conflicts are shown and handled using the standard Resolve Conflicts dialog box. There is a good article on MSDN about this type of merging, be sure to read it before continuing:

How To: Perform a Baseless Merge in Visual Studio Team Foundation Server
http://msdn.microsoft.com/en-us/library/bb668976.aspx

Tags: , ,

author: Ruud | posted @ Thursday, June 10, 2010 12:27 PM | Feedback (0)

Loading web.config section from different processes


SharePoint code can run on different processes because not all code is executed in the Application Pool. Here are a some examples:

  • SPFeatureReceiver code:
    Normally runs under the Application Pool process (W3WP.exe), but when a feature is activated with an stsadm command, the process is the executable STSADM.exe. 
  • SPItemEventReceiver code:
    Normally runs under the Application Pool process (W3WP.exe), but for example when an email is sent to a document library the code is run not in the but in the OWSTIMER.exe process (that is where the timer job picks up the mail and adds it to the library). 
  • SPJobDefinition code:
    Timer job code is executed by the OWSTIMER.exe process.

 

Sometimes it might be necessary to fetch some configuration set in the web config for configuration settings or sections that are not persisted in the SharePoint content database. In that case you will have to use the OpenWebConfiguration method of the WebConfigurationManager class to get the config file.
 

Accessing config section in Feature Receiver
The featurereceiver can run under the processes W3WP.exe and STSADM.exe, to get the webapplication, we can use the feature parent, in this case it is a site scoped feature, so the parent is SPSite. If it would be web we could get it by calling SPWeb.Site.

MyConfigurationSection section = ConfigurationManager.GetSection("SectionName") as MyConfigurationSection;

if (section == null)
{
    SPSite site = properties.Feature.Parent as SPSite;

    Configuration config = WebConfigurationManager.OpenWebConfiguration("/", site.WebApplication.Name);
    section = config.GetSection("SectionName") as MyConfigurationSection;
                    
    if(section == null) 
       throw new ConfigurationErrorsException("Could not load configuration section.");
}

Accessing config section in Event Receiver
This code can run under the process W3WP.exe, and OWSTIMER.exe. We can first try to get the config by using the standard ConfigurationManager, if that fails, and is null, we can try getting it by loading the configurationmanager from the corresponding site.

MyConfigurationSection section = ConfigurationManager.GetSection("SectionName") as MyConfigurationSection;

if (section == null)
{
   using(SPSite site = new SPSite(properties.SiteId))
   {
      Configuration config = WebConfigurationManager.OpenWebConfiguration("/", site.WebApplication.Name);
      section = config.GetSection("SectionName") as MyConfigurationSection;
   }
                   
   if(section == null) 
      throw new ConfigurationErrorsException("Could not load configuration section.");
}

Accessing config section in Timer Job
Timer job code runs under the process OWSTIMER.exe, so we should not try to get it by using the standard ConfigurationManager but get it from the web application directly.

SPWebApplication webApplication = properties.Feature.Parent as SPWebApplication;

Configuration config = WebConfigurationManager.OpenWebConfiguration("/", webApplication.Name);

MyConfigurationSection section = config.GetSection("SectionName") as MyConfigurationSection;
                    
if(section == null)
   throw new ConfigurationErrorsException("Could not load configuration section.");

Tags: ,

author: Ruud | posted @ Friday, June 04, 2010 12:10 PM | Feedback (0)