Tuesday, December 13, 2011

How to display source site title in Content Query Web Part and Search Results?

What?

How to display source site title in Content Query Web Part and Search Results?

Why?

It is a not so uncommon requirement to group documents / list items by sites and display the site title for each group.

Content Query Web Part returns lot of data but site title is not returned by it by default.

SharePoint search service application does not come with property for site name or site title by default but luckily sharepoint search crawls and indexes site name as ows_sitename

Wednesday, November 30, 2011

SharePoint 2010 calendar's "Add" button issues after fixing the scrolling bug

What?

How to fix SharePoint 2010 calendar's "Add" button issues after fixing the scrolling bug

Why?

Every SharePoint designer must have bumped across the scrolling issue with SharePoint 2010 master pages.

Kyle Schaeffer & Greg Galipeau have already written excellent post 1 & excellent post 2 about this issue and how to fix it and so I am not going into details of the issue and how to fix it.

Of course, with any solution that changes the core way a system works, there is bound to be issues. One such issue is with the OOB calendar web part's "Add" button.

Wednesday, November 16, 2011

SharePoint 2010 - How to deactivate and delete a sandbox solution (site template) that throws exception from UI?

What?


How to deactivate and delete a sandbox solution (site template) that throws exception from UI?

Why?


Created a SharePoint 2010 team site (web) and saved it as a site template.
Created a new site using the newly created site template.
Delete the site with which the site template was created.

Attempted to deactivate the site template and it fails with the below exception:

Unable to access web scoped feature (Id: GUID) because it references a non-existent or broken web (Id: GUID) on site 'url'.  Exception: System.ArgumentException: Value does not fall within the expected range. 
   at Microsoft.SharePoint.SPWebCollection.get_Item(Guid id)
   at Microsoft.SharePoint.SPFeatureEnumeratorBase.GetCachedWeb(SPSite site, Guid  webId, Guid featureId)

Friday, November 11, 2011

How to fix the Visual Studio Exception: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

What?


How to fix the Visual Studio Exception: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

Why?

Visual Studio (2010 in my case) throws an exception: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

I received this exception whenever I tried to debug even a basic solution. That means an empty Visual Studio solution using "ASP.NET 3.5 Website Project" template.

Saturday, October 1, 2011

SharePoint 2010 - Server error: The URL is invalid, it may refer to a nonexistent file or folder or refer to a valid file that is not in the current Web

Scenario:

SharePoint Designer throws the error "Server error: The URL is invalid, it may refer to a nonexistent file or folder or refer to a valid file that is not in the current Web" when you attempt to open/check-in/check-out/upload a file.

Explanation:

While there could be many reasons for this misleading error to show up, one of the reason is low disk space in the database server.

SharePoint 2010 - How to specify a different master page for a publishing page layout

What?

How to associate a different master page than the default master page for a publishing page layout in SharePoint 2010?

Why?

There may be scenarios when certain pages in a SharePoint site require associations with a different master page(s)..

Typical examples could be pages that are required to be loaded inside SharePoint dialog boxes. These type of pages show up fine with minimal.master master page associated with them.

How?

Applying “minimal.master” master page to a page layout "CustomDialogPages.aspx" did not work.

When the page layout inherits from the 'PublishingLayoutPage' class the master page gets set on PreInit(), that's why it ignores what we put in the page directive.

There are 2 possible ways to get this to work:

Wednesday, August 17, 2011

A shortcut to debug SharePoint 2010 Solutions

What?

All of us who worked on developing SharePoint 2010 solutions are aware of the frustration involved with debugging them.

Sunday, August 14, 2011

Slides from SharePoint Saturday: The Conference DC on 12th August 2011


Here are the slides from Slides from my SharePoint session given on August 12 2011 at SharePoint Saturday The Conference in Washington DC titled "Leverage Search and Customize to your Brand within SharePoint 2010" as promised.

Topic: 

Leverage Search and Customize to your Brand within SharePoint 2010

Details:

The enterprise search capabilities built in to SharePoint 2010 provide an easy win for organizations seeking to achieve a quick return on their SharePoint investment. This technical deep dive will include a discussion of search configuration best practices, strategies for maximizing SharePoint's capabilities, and simple customizations that can take your search experience to the next level.





Tuesday, August 2, 2011

How to delete a cookie written by ASP.NET using client-side JavaScript

What?

Today I spent some time trying to understand why a JavaScript function that I wrote to delete a cookie that was written by ASP.NET was not deleting it properly.

I finally managed to find the reason and thought it might be useful to write about it as it might help someone..


Tuesday, July 19, 2011

SharePoint : System.InvalidOperationException: The farm is unavailable.

Scenario:

You receive the below exception when you try to access Central Admin / any SharePoint site.

Explanation:

Event ID from Event Log - 3


System.ServiceModel.ServiceActivationException: The service '/SecurityTokenServiceApplication/securitytoken.svc' cannot be activated due to an exception during compilation.  The exception message is: Exception has been thrown by the target of an invocation.. ---> System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.InvalidOperationException: The farm is unavailable.
   at Microsoft.SharePoint.Administration.Claims.SPSecurityTokenServiceManager.get_Local()
   at Microsoft.SharePoint.IdentityModel.SPSecurityTokenServiceConfiguration..ctor()


Resolution:
  • Open IIS
  • Go to Application Pools
  • Find the security token application pool and click on "Advanced Settings"
  • If you notice that the value for “enable 32-bit applications” was set to true, change this to false

Thursday, July 14, 2011

Speaking at SharePoint Saturday The Conference on Aug 13 2011

I will be speaking at SharePoint Saturday The Conference on Aug 13 2011

Topic: 

Leverage Search and Customize to your Brand within SharePoint 2010

Details:

The enterprise search capabilities built in to SharePoint 2010 provide an easy win for organizations seeking to achieve a quick return on their SharePoint investment. This technical deep dive will include a discussion of search configuration best practices, strategies for maximizing SharePoint's capabilities, and simple customizations that can take your search experience to the next level.

Location:

Nothern Virginia Community College http://www.spstc.org/Pages/About.aspx

Link: 

http://www.spstc.org/SitePages/Sessions.aspx?SessionID=28

Sunday, July 10, 2011

SharePoint 2010 - Search Error - The search service is not able to connect to the machine that hosts the administration component

Problem:


We recently ran across an issue that prevented the SharePoint Server 2010 from starting with the below exception:

"The search service is not able to connect to the machine that hosts the administration component"


When I clicked on any link in the left navigation in search administration interface inside Central Administration, I was greeted with the below exception:


The application pool in IIS that the search service uses is found in stopped state. It stops automatically as soon as it is started.



Explanation:

After hours of research, we have found that it was all happening due to an AD policy set in domain controller that was not allowing certain service accounts from being able to start services and application pools

Here are the steps we followed to fix this issue:

  • First we did a complete rebuild including the OS
  • Then the search service worked but a service account was not able to start search service and the Web Services Default App Pool.. (sp2010sa account in our case)
  • We then updated the group policies in AD to allow the service accounts to have "logon as a batch" job rights
  • We had a problem in pushing the group policy to client(s). For that change to take place in the servers in the domain, we had to take the servers out of the domain and re add them..
  • So we removed computer(s) from domain, computer account was not created in the correct Organizational Unit(OU). New policy was forced as an update on the computer. So we removed the computer from the domain and rejoined to the domain..
  • In this process SharePoint Config DB got corrupted and so CA was gone
  • So we uninstalled SP 2010 again, removed all the SP databases and re-installed SP 2010

Summary:

In short, before installing SharePoint, make sure that you follow the least privileged approach and create all required service accounts. This article here  describes it very well.

Make sure that the managed accounts used such as SharePoint Search, SharePoint App Pool and the SharePoint Farm Account have "logon as a batch" job rights.

Server(s) can be removed from domain but computer accounts should not be removed. If computer account(s) are removed, the identifier(s) will be deleted and therefore the identifiers change the next time when the servers are added again to the domain controller. SharePoint looks for the identifiers in config database and since it could not find the correct identifiers, CA would not work.

If you have to apply new policy such as "logon as a service", make sure to place the computer(s) in correct Organizational Unit(OU) and remove the computer(s) from domain. The policy is then reset and it assigns the correct "logon as a service" credentials.

This wont be the case in most installations.
In our case, we ran into issues due to incorrect / corrupt AD structure / domain controller.

I am not a network specialist but if there is a way to ensure that all the AD group policies are being applied properly prior to installing SharePoint, you would save yourself from the above mentioned hassles.

Wednesday, June 8, 2011

SharePoint 2010 - Issues with Styles and Markup Styles drop downs in Rich Text Editor. Access is denied / 'undefined' is null or not an object - sp.ui.rte.js


Problem:

In the Rich Text Editor (CEWP / Page Content Area), when a user clicks on Markup Styles or Styles, the popups do not show up. A JS error shows up. (Access is denied / 'undefined' is null or not an object - sp.ui.rte.js)

Click inside a Rich Text Editor (CEWP or Page Content Field)



Click on Styles / Markup Styles



You will notice that the status bar reports a JS error.


Clicking on the error in status bar brings the below dialog:


Explanation:

I initially thought that the jQuery registration call or addThis script might be causing the issues and therefore tried removing jQuery references from Google and replaced them with local references, removed "Add This" widget, etc..

Finally it turned out to be that there are some empty CSS/JavaScript files in the master page that are causing the js error.

Resolution:

Make sure that there are no empty css files that are included in the master page.
Just add a comment like /* Styles */ in the CSS file
Check-In/Publish the CSS file and the error should disappear.

Tuesday, May 31, 2011

SharePoint - Server error: The URL is invalid, it may refer to a nonexistent file or folder or refer to a valid file that is not in the current Web



Scenario:


SharePoint Designer throws the error "Server error: The URL is invalid, it may refer to a nonexistent file or folder or refer to a valid file that is not in the current Web" when you attempt to open/check-in/check-out/upload a file.

Explanation:

While there could be many reasons for this misleading error to show up, one of the reason is low disk space in the database server.

I noticed that the transaction log file for the SharePoint_Config database, "SharePoint_Config_Log.LDF" (resides in \Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data in our case) has grown enormously to 28 GB which is approximately 70% of the space in that drive.

Solution:

Take a backup of the log file it required.
Just run the below scripts to truncate the log file:

SQL Server 2008:

USE [master]
GO
ALTER DATABASE[SharePoint_Config] SET RECOVERY SIMPLE WITH NO_WAIT
GO
USE [SharePoint_Config]
GO
DBCC SHRINKFILE ('SharePoint_Config_Log')
GO
ALTER DATABASE[SharePoint_Config] SET RECOVERY FULL WITH NO_WAIT
GO
USE [SharePoint_Config]
GO

SQL Server 2005:

BACKUP LOG [Sharepoint_Config] WITH TRUNCATE_ONLY
USE [SharePoint_Config]
GO
DBCC SHRINKFILE (N’SharePoint_Config_log’ , 50)
GO

The file "SharePoint_Config_Log.LDF" is now 504 KB.
The server is happy now and running fine.


Friday, May 27, 2011

SharePoint 2010 - Buggy SPSecurityTrimmedControl and a workaround

Scenario:


The SPSecurityTrimmedControl control in SharePoint is incomplete and buggy.
Information about the bugs in this control are not provided by MSDN either.

In this post, I will try to explain the bug in the SPSecurityTrimmedControl and a solution to get over it.

Explanation:


The SPSecurityTrimmedControl  allows us to display/hide the content using a few different criteria, like authentication (for anonymous/authenticated users only), page mode (page in display or edit mode) or current user’s permissions.

So what's wrong?


The control doesn’t work the way you would expect it.
It does a good job with displaying content based on permissions but fails to conditionally display content based on authentication(anonymous/authenticated users).

That means using this control, you can hide an element from users who do not have the appropriate permissions.
Srini Sistla has a good post explaining all the available permissions here

This control also has the so called ability to show/hide content based on authentication(anonymous/authenticated users).

You would need to use the property "AuthenticationRestrictions" which can take any of these values:


  • AllUsers
  • AuthenticatedUsersOnly
  • AnonymousUsersOnly


For Ex:

<SharePoint:SPSecurityTrimmedControl runat="server" id="stc" AuthenticationRestrictions="AnonymousUsersOnly"> <p>User is not logged in</p></SharePoint:SPSecurityTrimmedControl>

In case if you try to use this control in the above manner, it wont give you the desired results.

Examine the below screenshots and you understand it better.

Test 1: Tried with "AnonymousUsersOnly"


I tried to access the page as an anonymous user and below is the output. So test failed


Test 2: Tried with "AuthenticatedUsersOnly"


I tried to access the page as an authenticated user and below is the output. So test passed


Solution:


The solution is to use the LoginView control if you need to show/hide content based on authentication.
The LoginView control provides the following templats:

  • AnonymousTemplate - Controls to show to anonymous users only
  • LoggedInTemplate - Controls to show to authenticated users only

Test 3: Tried both AnonymousTemplate & LoggedInTemplate

I tried to access the page as an authenticated user and below is the output. Test passed
I tried to access the page as an anonymous user and below is the output. So test passed



Sunday, May 22, 2011

SharePoint 2010 - Access denied for users that have full control on the site

Scenario:
  • Users get "Access Denied" over the whole site, despite having Full Control permission
  • Site Collection Administrators have no problem logging in
Explanation:

A SharePoint 2010 site that uses claims-based authentication has been extended to Intranet zone that uses AD as well as FBA. The site has number of users in the default owners and members groups.

All the site users always get access denied over the whole site even though they clearly have access to the site through the site groups.

Site Collection Administrators are allowed to access the site and have no problems logging in.

Resolution:


Make sure that all the Master page, CSS files, any other files that are required are published.
If there are files that are required in the master pages and are not published, users will get access denied even if they have full control on the site.

If you are ok with giving all authenticated users atleast road-only access to all files inorder to prevent the access denied problem, then you can try the below.

Add a new "User Policy" for the web application that allows "All Authenticated Users" the permissions "Full Read" on the desired zone.

To add a new User Policy:


Go to Central Administration
Click on Application Management
Click on Manage Web Applications

Choose the desired web application:


Click on "User Policy"


Click on "Add Users" to add a new User Policy:



Select the zone that you want to apply the new policy to. If you are not sure what to choose, leave the defaut value selected (All Zones) and cick next


Choose the permissions that you would like to give to the user(s) in this new user policy. If you do not want to give the user(s) full permission, choose "Full Read". This permission ensures that all the users in this policy can atleast access the site. Then click on "Browse" icon in the "Choose Users" area.


If you would ike to give All Authenticated Users / AD users / FBA users "Full Read" access, then choose the appropriate group(s).
In the next screens, click OK to get out of the wizard.
Your new user policy should be ready now.

Tuesday, May 17, 2011

Sharepoint 2010 - An exception occurred when trying to issue security token: The server was unable to process the request due to an internal error

Scenario:

You receive the below exception when you try to logon to a site that has been configured to use Claims Based Authentication with a custom membership provider using FBA credentials:

Event ID from Event Log  - 8306

An exception occurred when trying to issue security token: The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the <serviceDebug> configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs..

Explanation:

This error started to appear in our QA environment which does not have Visual Studio installed. I have tried starting the service "Claims to Windows Token Service" but that did not help either.

I have made sure that all configuration changes required for FBA have been made properly in the below web.config files:
  • Web Applications
  • CA Web Aplication
  • \14\WebServices\SecurityToken
This post can get you started with building a custom membership provider and making changes to the required configuration files.



Resolution:

To view more information about the actual error that is preventing the secure token service from being able to issue security token, I added the service debug in the web.config for the web service, under \14\WebServices\SecurityToken:
<serviceDebug includeExceptionDetailInFaults="True" httpHelpPageEnabled="True"/>
After adding the above to SecurityToken's web.config, the event log reported the below:


An exception occurred when trying to issue security token: The configuration section for Logging cannot be found in the configuration source..

I then realized that it is Microsoft.Practices.EnterpriseLibrary.Logging from my custom membership code that is causing the issues and not FBA configuration as I originally thought.

I then went ahead and added the required configuration for Microsoft.Practices.EnterpriseLibrary.Logging

Everything after that worked as desired. 

So I strongly advice everyone not to panic and take such drastic steps as re-installing SharePoint 2010 etc..
When I googled for this error, many people suggested to reinstall SharePoint 2010, change server names, etc..

Remember that these things happen for a reason. It just requires some patience to figure out the exact problem.

Sunday, March 27, 2011

How to fix troubles with internet access in a Windows Server 2008 Std VM that is configured as a domain controller

Scenario:
No internet in domain controller Windows Server 2008 VM? How to fix troubles with internet access in a VM that is configured as a domain controller?

Explanation:
Installed SharePoint Foundation in a Windows Server 2008 Std VM.
Before configuring the server as a domain controller, the VM had access to internet.
Soon after configuring the VM as a domain controller, the server could not access internet from the host machine.

Solution:
After hours of troubleshooting, the below worked :

Go to Control Panel\Network and Internet\Network and Sharing Center

Click Local Network Connection:


Click Properties:


Highlight Internet Protocal Version 4 (TCP/IPv4) and click Properties:



Make sure to choose "Obtain IP Address automatically" and "Obtain DNS server address automatically"

Repeat the same step for IPv6 as well..

Disclaimer: I am not a network admin. This just happened to work for me. I am sharing this so that it could just save some time for some one with the same problem.

PowerShell - How to use named optional parameters in PowerShell?

Scenario:
How to use named optional parameters in PowerShell?

Explanation:
I always wondered whether it is possible to use named optional parameters in PowerShell scripts.
The below script explains how to accomplish it.

Solution:
param([string]$SolutionPath = "", [string]$WebApp = "")
The above line tells PowerShell to extract values from parameters "SolutionPath" and "WebApp".
These values will be used in PowerShell with the same names ($SolutionPath and $WebApp)

Lets save the below code into a ps1 file and name it "NamedParameters.ps1"
#IMPORTANT - The below line should be the first line. There should be nothing before the below line.
param([string]$SolutionPath = "", [string]$WebApp = "")

Write-Host "Solution Path: $SolutionPath"
Write-Host "Web App: $WebApp"
Run the script that we just developed:
.\NamedParameters.ps1 -SolutionPath "D:\Ironworks.SharePoint2010.Features.Core.wsp" -WebApp "http://ironworksdev.com" 
Output:
Solution Path: D:\Ironworks.SharePoint2010.Features.Core.wsp
Web App: http://ironworksdev.com
.\NamedParameters.ps1 -SolutionPath "D:\Ironworks.SharePoint2010.Features.Core.wsp" 
Output:
Solution Path: D:\Ironworks.SharePoint2010.Features.Core.wsp
Web App:
.\NamedParameters.ps1 -WebApp "http://ironworksdev.com" 
Output:
Solution Path:
Web App: http://ironworksdev.com

Hope that this helps someone.

How to build a dynamic query for SPQuery with multiple OR conditions programatically?

Scenario:
How to build a dynamic query for SPQuery object with multiple OR conditions programatically?

Explanation:
In this post, I will try to explain how to build a query for SPQuery object with multiple OR conditions dynamically.

For example: If a programmer attempts to get list items from a list with IDs 1,2,5, 9 and 13 dynamically, he would need to build a query for SPQuery with multiple OR conditions.

The below query seems to work at first glance but it does not work:
<Query>
  <Where>
    <Or>
      <Eq>
        <FieldRef Name="ID" />
        <Value Type="Counter">1</Value>
      </Eq>
      <Eq>
        <FieldRef Name="ID" />
        <Value Type="Counter">2</Value>
      </Eq>
      <Eq>
        <FieldRef Name="ID" />
        <Value Type="Counter">5</Value>
      </Eq>
      <Eq>
        <FieldRef Name="ID" />
        <Value Type="Counter">9</Value>
      </Eq>
      <Eq>
        <FieldRef Name="ID" />
        <Value Type="Counter">13</Value>
      </Eq>
    </Or>
  </Where>
</Query>

The reason why the above query does not work is because there is a hard limit set by SharePoint's SPQuery object to 2 items within an OR condition. The above code would only work for maximum of 2 items:
<Query>
  <Where>
    <Or>
      <Eq>
        <FieldRef Name="ID" />
        <Value Type="Counter">1</Value>
      </Eq>
      <Eq>
        <FieldRef Name="ID" />
        <Value Type="Counter">2</Value>
      </Eq>
    </Or>
  </Where>
</Query>

The correct query for SPQuery should look like the below:
<Query>
  <Where>
    <Or>
      <Eq>
        <FieldRef Name="ID" />
        <Value Type="Counter">1</Value>
      </Eq>
      <Or>
        <Eq>
          <FieldRef Name="ID" />
          <Value Type="Counter">2</Value>
        </Eq>
        <Or>
          <Eq>
            <FieldRef Name="ID" />
            <Value Type="Counter">5</Value>
          </Eq>
          <Or>
            <Eq>
              <FieldRef Name="ID" />
              <Value Type="Counter">9</Value>
            </Eq>
            <Eq>
              <FieldRef Name="ID" />
              <Value Type="Counter">13</Value>
            </Eq>
          </Or>
        </Or>
      </Or>
    </Or>
  </Where>
</Query>

Solution:
The below code could be used to build a dynamic query for SPQuery object in SharePoint that works with multiple OR conditions:

First we need a data structure that holds all the list item ids.
For this demo, I will create a custom object called "CustomSPItem" and use a generic list collection of my custom objects as data source.

My custom class:
class CustomSPItem
{
    /// <summary>
    /// ID from the SP List
    /// </summary>
    public int Id
    {
        get;
        set;
    }

    //Other Properties here

    public CustomSPItem()
    {
    }

    public CustomSPItem(int id)
    {
        Id = id;
        //Other properties here..
    }
}

Now create a collection of CustomSPItem objects:
List lstCustomSPItems = new List();
lstCustomSPItem.Add(new CustomSPItem(1));
lstCustomSPItem.Add(new CustomSPItem(2));
lstCustomSPItem.Add(new CustomSPItem(5));
lstCustomSPItem.Add(new CustomSPItem(9));
lstCustomSPItem.Add(new CustomSPItem(13));

Time to build dynamic query for SPQuery.
String query = BuildDynamicSPQueryWithMultipleOrConditions(lstCustomSPItems);

Required methods:
#region Build Dynamic SP Query with Multiple Or Conditions
/// <summary>
/// Builds SPQuery with multiple Or conditions
/// TODO: Describe this in more detail
/// </summary>
/// <param name="lstCustomSPItems"></param>
/// <returns></returns>
public static String BuildDynamicSPQueryWithMultipleOrConditions(List<CustomSPItem> lstCustomSPItems)
{
    String query = String.Empty;

    try
    {
        XmlDocument xmlDoc = new XmlDocument();
        XmlElement nodeWhere;

        //Create root node SPListItems
        nodeWhere = xmlDoc.CreateElement("Where");
        xmlDoc.AppendChild(nodeWhere);

        XmlElement nodeOr = null;
        int locCtr = 0;

        if (lstCustomSPItems.Count == 1)
        {
            var customSPItem = lstCustomSPItems[0];

            XmlElement nodeEq = BuildEqNodeForSPQuery(ref xmlDoc, ref nodeWhere);

            BuildEqNodeInnerXmlForSPQuery(ref xmlDoc, ref nodeEq, customSPItem.Id.ToString());
        }
        else
        {
            foreach (var customSPItem in lstCustomSPItems)
            {
                //Increment counter. We will need it to find the last item
                locCtr++;

                if (locCtr == 1)
                {
                    nodeOr = BuildDynamicOrEqCombination(ref xmlDoc, ref nodeWhere, customSPItem.Id.ToString());
                }
                else if (locCtr == lstCustomSPItems.Count)
                {
                    //We will need to include the last 2 nodes in the Or node. Is this the last record?
                    UpdateOrNode(ref xmlDoc, ref nodeOr, customSPItem.Id.ToString());
                }
                else
                {
                    nodeOr = BuildDynamicOrEqCombination(ref xmlDoc, ref nodeOr, customSPItem.Id.ToString());
                }
            }
        }

        query = xmlDoc.InnerXml;
    }
    catch (Exception ex)
    { }

    return query;
}

/// <summary>
/// Update Or node with a new Eq node
/// </summary>
/// <param name="xmlDoc"></param>
/// <param name="nodeParent"></param>
/// <param name="id"></param>
private static void UpdateOrNode(ref XmlDocument xmlDoc, ref XmlElement nodeParent, String id)
{
    XmlElement nodeEq = BuildEqNodeForSPQuery(ref xmlDoc, ref nodeParent);
    nodeParent.AppendChild(nodeEq);

    BuildEqNodeInnerXmlForSPQuery(ref xmlDoc, ref nodeEq, id.ToString());
}

/// <summary>
/// Build Xml node with a combination of Or and Eq
/// </summary>
/// <param name="xmlDoc"></param>
/// <param name="nodeParent"></param>
/// <param name="id"></param>
/// <returns></returns>
private static XmlElement BuildDynamicOrEqCombination(ref XmlDocument xmlDoc, ref XmlElement nodeParent, String id)
{
    XmlElement nodeOr = BuildOrNodeForSPQuery(ref xmlDoc, ref nodeParent);

    XmlElement nodeEq = BuildEqNodeForSPQuery(ref xmlDoc, ref nodeOr);

    nodeOr.AppendChild(nodeEq);

    BuildEqNodeInnerXmlForSPQuery(ref xmlDoc, ref nodeEq, id.ToString());

    return nodeOr;
}

/// <summary>
/// Build Xml node for "Or"
/// </summary>
/// <param name="xmlDoc"></param>
/// <param name="nodeListItem"></param>
private static XmlElement BuildOrNodeForSPQuery(ref XmlDocument xmlDoc, ref XmlElement nodeParent)
{
    XmlElement nodeOr = null;
    try
    {
        nodeOr = xmlDoc.CreateElement("Or");
        nodeParent.AppendChild(nodeOr);
    }
    catch (Exception ex)
    { }
    return nodeOr;
}

/// <summary>
/// Build Xml node for "Eq"
/// </summary>
/// <param name="xmlDoc"></param>
/// <param name="nodeListItem"></param>
private static XmlElement BuildEqNodeForSPQuery(ref XmlDocument xmlDoc, ref XmlElement nodeParent)
{
    XmlElement nodeEq = null;
    try
    {
        nodeEq = xmlDoc.CreateElement("Eq");
        nodeParent.AppendChild(nodeEq);
    }
    catch (Exception ex)
    { }
    return nodeEq;
}

/// <summary>
/// Build Xml node for "Eq"
/// </summary>
/// <param name="xmlDoc"></param>
/// <param name="nodeParent"></param>
private static void BuildEqNodeInnerXmlForSPQuery(ref XmlDocument xmlDoc, ref XmlElement nodeParent, String id)
{
    try
    {
        XmlElement nodeFieldRef = xmlDoc.CreateElement("FieldRef");
        nodeFieldRef.SetAttribute("Name", "ID");

        XmlElement nodeValue = xmlDoc.CreateElement("Value");
        nodeValue.SetAttribute("Type", "Counter");
        nodeValue.InnerText = id;

        nodeParent.AppendChild(nodeFieldRef);
        nodeParent.AppendChild(nodeValue);
    }
    catch (Exception ex)
    {
    }
}
#endregion

The final query that is generated would look like the below:
<Query>
  <Where>
    <Or>
      <Eq>
        <FieldRef Name="ID" />
        <Value Type="Counter">1</Value>
      </Eq>
      <Or>
        <Eq>
          <FieldRef Name="ID" />
          <Value Type="Counter">2</Value>
        </Eq>
        <Or>
          <Eq>
            <FieldRef Name="ID" />
            <Value Type="Counter">5</Value>
          </Eq>
          <Or>
            <Eq>
              <FieldRef Name="ID" />
              <Value Type="Counter">9</Value>
            </Eq>
            <Eq>
              <FieldRef Name="ID" />
              <Value Type="Counter">13</Value>
            </Eq>
          </Or>
        </Or>
      </Or>
    </Or>
  </Where>
</Query>

Thursday, March 24, 2011

How to build a consumer web part in SharePoint 2010 using IWebPartParameters that works with HTML Form Web Part and Filter Web Parts?

Scenario:
How to build a consumer web part in SharePoint 2010 using IWebPartParameters that works with HTML Form Web Part and Filter Web Parts?

Explanation:
The current SharePoint 2010 documentation still includes reference to an API interface which it says is obsolete.

[ObsoleteAttribute("Use System.Web.UI.WebControls.WebParts.IWebPartParameters instead")]
public interface IFilterConsumer

As we can see, the interface displays a message that says "Use System.Web.UI.WebControls.WebParts.IWebPartParameters instead."
Unfortunately, it's hard to find any good examples out there for writing a Web Part that uses the IWebPartParamters interface for consuming the HTML Form Web Part / Filter Web Parts.

There is one brilliant example here but I had to tweak it slightly to get it working.

Solution:
For the purpose of this demo, I need to capture value for "Zip Code" from the provider web part which is an HTML Form Web Part.

We will need to create a public property in the web part called "ProviderZipCode".

Code:

[ToolboxItemAttribute(false)]
    public class DemoWebPart : System.Web.UI.WebControls.WebParts.WebPart, IWebEditable, IWebPartParameters
    {

        #region Private Members

        private List<IFilterValues> _filterProviders;

        IWebPartParameters _provider = null;
        IDictionary _data = null;
        
        #endregion

        #region Public Properties
        
        /// <summary>
        /// Zip Code from provider web part
        /// </summary>
        public string ProviderZipCode
        {
            get;
            set;
        }
        
        #endregion

        #region Constructor
        
        public DemoWebPart()
        {
            _filterProviders = new List<IFilterValues>();
        }
        
        #endregion

        #region Control Events
        
        protected override void OnPreRender(EventArgs e)
        {
            if (this._provider != null)
            {
                this._provider.GetParametersData(new ParametersCallback(SetProviderData));
            }

            //Get the value (zip code) from the provider web part
            String zipCodeFromProvider = GetFilterValue();

            base.OnPreRender(e);
        }
        
        #endregion

        #region IWebEditable Members
        
        EditorPartCollection IWebEditable.CreateEditorParts()
        {
            //Return the editor part used with this web part.  This builds a
            //custom editor with dropdowns and an asset url selector.

            List<EditorPart> editors = new List<EditorPart>();
            editors.Add(new DemoEditorPart());
            return new EditorPartCollection(editors);
        }
        
        #endregion

        #region Webpart Connections - Consumer

        [ConnectionConsumer("Parameters Data")]
        public void SetProvider(IWebPartParameters provider)
        {
            if (provider != null)
            {
                PropertyDescriptorCollection props = TypeDescriptor.GetProperties(this);
                PropertyDescriptor p1 = props.Find("ProviderZipCode", false);

                PropertyDescriptorCollection schemaProps = new PropertyDescriptorCollection(new PropertyDescriptor[] { p1 });

                provider.SetConsumerSchema(schemaProps);
                this._provider = provider;
            }
        }

        public void SetProviderData(IDictionary dict)
        {
            this._data = dict;
        }

        private string GetFilterValue()
        {
            if (this._provider != null && this._data != null)
            {
                if (_data["ProviderZipCode"] != null)
                {
                    ProviderZipCode = _data["ProviderZipCode"].ToString();
                    return ProviderZipCode;
                }
            }
            return "";
        }

        public void SetConsumerSchema(PropertyDescriptorCollection schema)
        {
            throw new Exception("Not implemented.");
        }

        public void GetParametersData(ParametersCallback callback)
        {
            throw new Exception("Not implemented.");
        }

        public PropertyDescriptorCollection Schema
        {
            get { throw new Exception("Not implemented."); }
        }

        #endregion
    }

After the web part is deployed, I have added an HTML Form Web Part.


When we attempt to connect the HTML Web Part (Provider) to the web part we just deployed (I gave it's title "Find Nearest Locations From A ZipCode" ), we can see our web part in the list of connectible web parts.

In the next screen, we see the provider and consumer field names.
As we can notice in the below picture, our custom property "ProviderZipCode" shows up in the consumer field name and T1, the name of the Text Box in the HTML Form Web Part shows up in the provider field name..



How to call SharePoint Web Services from the context of current logged in user from an asp.net web application?

Scenario:
How to call SharePoint Web Services from the context of current logged-in user from an ASP.NET Web Site/Application that has Windows Authentication enabled?

Explanation:
SharePoint 2010/2007 exposes most of its Object Model through web services.
SharePoint Web Services provide methods that you can use to work remotely with a deployment of SharePoint.

A list of SharePoint 2010 web services can be found here
A list of SharePoint 2007 web services can be found here


Its not always a straight forward process to call SharePoint Web Services from the context of current logged in user from an ASP.NET Web Site/Application that has Windows Authentication enabled.

In this post I will try to explain how to connect to SharePoint Web Services in both WCF and good old web service way.


Solution:


The WCF way:

Add a service reference to the web service (Ex: https://SharePointSite.com/_vti_bin/lists.asmx)
In this example, I have named it "ListsServiceReference"

            ListsServiceReference.ListsSoapClient listsServiceReference = new ListsServiceReference.ListsSoapClient();
            listsServiceReference.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
       
            listsServiceReference.Endpoint.Address = new System.ServiceModel.EndpointAddress("https://SharePointSite.com/_vti_bin/lists.asmx");

            try
            {
                //Check whether the current user has access to the site.
                //Assuming that all the authorized SharePoint users have access to the list "Form Templates", just check if the current user has access to the list.

                var list = listsServiceReference.GetList("Form Templates");

                _isUserAuthenticated = true;
            }
            catch (Exception ex)
            {
                //User does not seem to have access to the list.
                _isUserAuthenticated = false;
            }

Web.Config Modifications:

<system.serviceModel>
  <bindings>
   <basicHttpBinding>
    <binding name="ListsSoap" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
     <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
     <security mode="Transport">
      <transport clientCredentialType="Windows" />
     </security>
    </binding>
   </basicHttpBinding>
  </bindings>
  <client>
   <endpoint address="" binding="basicHttpBinding" bindingConfiguration="ListsSoap" contract="ListsServiceReference.ListsSoap" name="ListsSoap" />
  </client>
 </system.serviceModel>

The Web Service way:


Add a web reference to the SharePoint Web Service.
In this example, I have named it "ListsService"

                try
                {
                    ListsService.Lists listService = new ListsService.Lists();
                    listService.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
                    listService.Url = "https://SharePointSite.com/_vti_bin/lists.asmx";

                    //Check whether the current user has access to the site.
                    //Assuming that all the authorized SharePoint users have access to the list "Form Templates", just check if the current user has access to the list.
                    var listNodes = listService.GetList("Form Templates");

                    _isUserAuthenticated = true;
                }
                catch (Exception ex)
                {
                    //User does not seem to have access to the list.
                    _isUserAuthenticated = false;
                }

The important thing to do to make the ASP.NET web service calls work is to make sure that the site needs to have ASP.NET Impersonation and Windows Authentication enabled and that the site  runs in "Classic .NET AppPool" Application Pool.

Sunday, March 13, 2011

How to prevent asp.net UpdatePanel to prevent doing post backs from a custom control

Scenario: 
How to prevent asp.net UpdatePanel to prevent doing post backs from a custom control?

Explanation:
Asp.net UpdatePanel is primarily used for performing asynchronous postbacks with partial page updates. After adding an UpdatePanel control to your page, you can achieve a partial-update of your page on a postback. Only the content in the UpdatePanel is refreshed, the other parts of the page remain unchanged.

You may experience scenarios when controls that are placed inside an UpdatePanel cause the whole page to raise a post back.

Resolution:
Each control inside the update panel need to have an id assigned. After all the controls inside the update panel are assigned ids, the controls inside the UpdatePanel wont cause the page to raise post backs anymore.

How to enable VariationLabelMenu control in SharePoint 2010?

Scenario: 
How to enable VariationLabelMenu control in SharePoint 2010?

Explanation:
SharePoint 2010 (and MOSS 2007) ships with a control called VariationLabelMenu that can be used on sites with site variations enabled. This control enables users to navigate to the related/equivalent pages in other language sites (for which the language packs are installed)

Even though this control is provided OOB in SharePoint 2010, few steps need to be followed to get it to work.

After everything has been configured correctly, you will see a nice little menu that looks like in the below picture. You can place it in the master page or page layout according to your needs.


In my case, I have german language pack installed and therefore the VariationLabelMenu control displays "Deutsch" in the dropdown options.

Solution:

Step-1: 
Go to the location Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\CONTROLTEMPLATES, find the file "VariationsLabelMenu.ascx" and edit it.
Add the below line to the contents of the file.

<cms:VariationsLabelEcbMenu id ="varlabelmenu1" DataSourceID="LabelMenuDataSource" DisplayText="Links" IsCallbackMode="true" runat="server" />






After this change, the VariationLabelMenu shows up in the ribbon but it messes up with the existing controls in the ribbon which is not very pleasant.

The side effects due to the above change are as below:


As you can notice in the above picture, the page editing options have been messed up.
Ideally it should look like below:


Also the welcome control that displays the UserContextMenu will be replaced with the VariationLabelMenu control as shown below:



From what I have noticed, the reason for the disappearance of the welcome menu and the improper display of the page editing options in the ribbon are because of the VariationLabelMenu. So if we hide it in the ribbon and show it somewhere else on the page, things will look normal again.

I have searched for the VariationLabelMenu control's markup in the HTML generated by SharePoint. In my case, it is ctl00_ctl34_varlabelmenu1. The control ID may change depending on your master page settings
<style type="text/css">
      #ctl00_ctl34_varlabelmenu1 {display:none;}
</style>  
Include the above code any where in the master page / page layout and it will hide the VariationLabelMenu control in the ribbon.

Now it's time to add the VariationLabelMenu control somewhere else on the page.
Add the below line of code at the top of the master page.
<%@ Register TagPrefix="PublishingVariations" TagName="VariationsLabelMenu" src="~/_controltemplates/VariationsLabelMenu.ascx" %>  

Find an appropriate location in the master page / page layout and add the below line of code which renders the VariationLabelMenu on the page.
<PublishingVariations:VariationsLabelMenu id="labelmenu1" runat="server"/>
Your pages should now display the VariationsLabelMenu properly.

"The remote server returned an error: (401) Unauthorized." exception when you try to access a file in SharePoint Document Library

Scenario:
You receive an exception "The remote server returned an error: (401) Unauthorized." when you try to access a file in SharePoint Document Library.

Explanation:

When you try to access a document in a SharePoint site without anonymous access turned on in the below manner, you would receive a 401 unauthorized error.

XmlDocument xDocument = new XmlDocument();
xDocument.Load(String.Format("{0}{1}", SPContext.Current.Web.Url, XmlFileLocation));

Resolution:

Try to access the file as below:

SPFile configFile = SPContext.Current.Web.GetFile(String.Format("{0}{1}", SPContext.Current.Web.Url, XmlFileLocation));
XmlDocument xdoc = new XmlDocument();
byte[] fileBytes = configFile.OpenBinary();
Stream streamFile = new MemoryStream(fileBytes);
xdoc.Load(streamFile);






Saturday, January 8, 2011

How to estimate the storage space required for SharePoint 2010 content databases and Index servers?

Scenario:
How to estimate the storage space required for SharePoint 2010 content databases and index servers?

Explanation:

The following process describes how to approximately estimate the storage required for content databases, without considering log files:
1.   Calculate the expected number of documents. This value is referred to as D in the formula.

How you calculate the number of documents will be determined by the features that you are using. For example, for My Site Web sites or collaboration sites, we recommend that you calculate the expected number of documents per user and multiply by the number of users. For records management or content publishing sites, you may calculate the number of documents that are managed and generated by a process.

If you are migrating from a current system, it may be easier to extrapolate your current growth rate and usage. If you are creating a new system, review your existing file shares or other repositories and estimate based on that usage rate.
2.   Estimate the average size of the documents that you will be storing. This value is referred to as S in the formula. It may be worthwhile to estimate averages for different types or groups of sites. The average file size for My Site Web sites, media repositories, and different department portals can vary significantly.
3.   Estimate the number of list items in the environment. This value is referred to as L in the formula.

List items are more difficult to estimate than documents. We generally use an estimate of three times the number of documents (D), but this will vary based on how you expect to use your sites.
4.   Determine the approximate number of versions. Estimate the average number of versions any document in a library will have (this value will usually be much lower than the maximum allowed number of versions). This value is referred to as V in the formula.

The value of V must be above zero.
5.   Use the following formula to estimate the size of your content databases:

Database size = ((D × V) × S) + (10 KB × (L + (V × D)))

The value of 10 KB in the formula is a constant that roughly estimates the amount of metadata required by SharePoint Server 2010. If your system requires significant use of metadata, you may want to increase this constant.
As an example, if you were to use the formula to estimate the amount of storage space required for the data files for a content database in a collaboration environment with the following characteristics, you would need approximately 105 GB.
Input
Value
Number of documents (D)
200,000
Calculated by assuming 10,000 users times 20 documents
Average size of documents (S)
250 KB
List items (L)
600,000
Number of non-current versions (V)
2
Assuming that the maximum versions allowed is 10
Database size = (((200,000 x 2)) × 250) + ((10 KB × (600,000 + (200,000 x 2))) = 110,000,000 KB or 105 GB

Microsoft recommends 80 GB of hard disk space for WFE servers. Maintain twice as much free space as you have RAM for production environments.

A quick formula to calculate the hard disk space on the index server would be to multiply the expected index-able content by 20%.

So for example if the expected total content to be indexed is 500 GB, the index server needs at least 500 * 20% = 100GB

This white paper from Microsoft explains how to estimate performance and capacity requirements for SharePoint in more detail. (page 45 for specific guidance on how to calculate storage needs for search)