Reference Custom CSS and JavaScript files in SharePoint 2010

I have been building a custom .NET web part page to use in my SharePoint intranet. The .NET page has quite a lot of custom HTML and jQuery design elements, so using CSS and JavaScript files were essential.

As you know, when we want to use elements from our CSS and JavaScript files we normally add the following lines of HTML at the top of our page:

<!-- CSS -->
<link type="text/css" rel="stylesheet" href="site.css" />

<!-- JavaScript -->
<script src="jQuery.js" type="text/javascript" />

If you added those lines of code in a custom SharePoint page, you’ll find that the page will ignore them. Thankfully, SharePoint has given us some controls to add these references.

At this point its worth stating that I stored all my required JavaScript and CSS files within the “Style Library” directory situated in the root of any SharePoint 2010 intranet. In order to get these files I used controls called”CssRegistration” and “ScriptLink”:

<!-- CSS -->
<SharePoint:CssRegistration ID="CssRegistration1" Name="/Style Library/Home/CSS/jcarousel.css" runat="server" After="corev4.css" />

<!-- JavaScript -->
<SharePoint:ScriptLink ID="ScriptLink1" Name="~sitecollection/Style Library/Home/JS/jquery-1.4.4.min.js" runat="server" />

If you have stored your CSS and JavaScript within the physical file directory situated in the 14 hive folder, you will need to modify the above example to the following:

<!-- CSS -->
<SharePoint:CSSRegistration Name="<% $SPUrl:~SiteCollection/Style Library/Core Styles/jcarousel.css%>" runat="server"/>

<!-- JavaScript -->
<SharePoint:ScriptLink ID="ScriptLink1" Name="<% $SPUrl:~SiteCollection/Style Library/Core Styles/jquery.js%>" runat="server" />

The only difference between this example and our earlier example is that when we have just added “SPUrl” to get files relative to the current site collection.

Retrieve and Manage User Profile Properties in SharePoint 2010

I am writing a custom webpart that will output user profile information from SharePoint 2010. My code requires me to get quite a few fields. Most of these fields are not “intellisensable” and cannot be accessed directly without having to manually enter the field name, as you can see from my code snippet below.

User Profile Properties Code

But its really easy to get the user field properties incorrect. A good example, is retrieving the office location. You would think the property name would be called “OfficeLocation” but its actually called “SPS-Location”.

Luckily SharePoint allows us to view and access all the user profile properties we require and even create our own custom fields.

Lets start by opening Central Administration and navigate to Manage Service Applications > User Profile Service Application, which will take you to the following page:

User Profile Service Application

Click on “Manage User Properties” to view a list of all user field properties SharePoint uses. To either rename the display name or view the actual property name, click on a field and press “Edit”.

Manage User Properties

The “Name” field (as highlighted below) is not editable and for a very good reason too! These are the property ID’s that we will call when wanting to retrieve their value. All default field names are not editable.

Edit User Property

As I stated earlier, you can create your own properties and call them whatever you want. But SharePoint already provides us with so many out-of-the-box, you probably won’t need to create anymore anytime soon.

Enable People Search in SharePoint 2010

In my last post, I showed you how to create an Enterprise Search page that consisted of both “Site” and “People” searches. Depending on how you have setup your search within Central Administration, you may find the “People” search not returning any results.

Before we start, there are a few things you need to check. Firstly, ensure you have the necessary search services in working order. If you can carry out site searches you should be fine. Secondly, ensure the User Profile service has been setup sufficiently so that features such as MySites and Profile databases are working.

In a straight-forward world, you would think that completing the steps above would be enough for SharePoint 2010 to allow you to search users within your site. But sadly we don’t live in a straight-forward world.

Open Central Administration and navigate to “Manage Service Applications”. Within the list of services, select “Enterprise Search Service Application”.

Manage Services Enterprise Search

In the “Enterprise Search Service Application” page, click on the “Content Sources” link you’ll find situated in the left hand navigation and open/edit your “Local SharePoint Sites” content source.

Manage Content Sources

In the Start Addresses section, you will see a box with entries similar to what I have in my SharePoint intranet below..well almost the same:

Content Sources Start Addresses

You will notice the line: “sps3://my-intranet” which tells SharePoint to call a specific web service hosted at that web address. In this case, the URL is the same one I use to access my main site collection. When you have added the “sps3://” line yourself press the “OK” button to save your changes.

There is just one last step we need to carry out: re-indexing our search. Navigate back to the “Enterprise Search Service Application” page and start full crawl.

Manage Content Sources Recrawl

Once this has completed all your user profiles should now be searchable.

Enterprise People Search

Setup Enterprise Search Page in SharePoint 2010

Hooray! My first SharePoint 2010 blog post!

I have been lucky enough to start working on my first SharePoint 2010 project. As you may know, things have definitely moved on from SharePoint 2007 to SharePoint 2010. Every new release of SharePoint seems to be a vast improvement over its predecessor that benefits both the end users and developers. But just as things get better and better, you’ll find yourself falling into the common trap of trying to apply what you have learnt in SharePoint 2007 to SharePoint 2010. I know I did.

A good example of this is having a search page that allows users to search “All Sites” or “People”, something we would see in a SharePoint 2007 search page as standard:

MOSS 2007 Search

I was surprised to find out that this wasn’t the search I would get by default. The SharePoint 2010 search is quite basic and out-of-the-box as you can see from the screenshot below:

Sharepoint 2010 Original Search

In order to get a search page that includes both Site and People search (or as Enterprise Search as SharePoint 2010 now calls it), you have to carry out an additional step that simply requires creating a new site. So, go to “Site Actions” and click on “New Site”. When the popup opens, select the “Search” category and select “Enterprise Search”. Enter a page and name and URL name and click “Create”.

Sharepoint 2010 New Site Enterprise Search

If everything goes well, you should see a search page which looks like something like this:

Sharepoint 2010 Enterprise Search Page

Cool! So you now have the ability to carry out Site and People searches. But you may find the People search will not work if you carried out the same mistake I did where I missed out a key setting in Central Administration. I will blog about that within the next few days. TO BE CONTINUED...

Post Updated: 30/01/2011 - Enable People Search in SharePoint 2010

At Last! Created My Own eBay Style Search Using Solrnet

Over the last few months I have been carrying out endless amounts of research and development to find a way to create my own eCommerce styled search similar to the likes of what eBay and Amazon use. Otherwise known as “Faceted Search”, whereby the search results are filtered through a series of facets belonging to your search criteria. Each facet typically corresponds to the possible values of a property common to a set of objects.

Sounds very difficult and complex doesn’t it! Smile Even to this very day, I am sure eBay and Amazon must use some kind of “magic” to get their search to work in a seamlessly and efficient format.

There are numerous search solutions out there that could help you achieve in making this type of search. From my experience I couldn’t find any low cost out-of-the-box solutions that would help me in making my own search. Majority of the search vendors were not only very expensive but they also required a quote to tailor make a solution for you.

In the early stages I tried expanding my Lucene.NET knowledge, but I couldn’t find a flexible way to introduce facets into my search. I must admit I am not exactly an expert in Lucene and this could have also had a part to play in failing miserably.

When I thought all was lost and there was no chance in hell in being able to figure this thing out, I luckily came across a few blog and StackOverflow posts by a guy called Mauricio Scheffer. Mauricio seems to be the brains behind the .NET client version of a search platform called: SolrNet. SolrNet is a  Solr client library built for the .NET Framework. This is one of the strengths of Solr. It can be consumed within other development platforms such as Python and Ruby.

SolrNet just happened to be an ideal solution to what I was looking for and with just over a weeks development I was able to build my own basic search, which looks something like this:

SolrNet1 SolrNet2

As you can see from my screenshots, you can carry out a search by report type and/or global text search. In addition, the showing and hiding of the facet objects are purely dependent on the searches returned.

SolrNet is a very flexible package and I know just enough to implement the basics. But I was really surprised on how well the searches performed even with the most basic implementation. So I am looking forward to adding additional features as over the next few months and perfecting both my Solr search index and code.

I won’t be posting the code that I used to create my search since its quite a big project and tailor made specific to my database architecture. But here are a few links that I found useful to get me started in the world of SolrNet:

Multi Query Search Using Lucene.NET

Over the last few days I have been doing some research on the best way to implement search functionality for a site I am currently building. The site will consists mainly of news articles. The client wanted a search that would allow a user to search across all fields that related to a news article.

Originally, I envisaged writing my own SQL to query a few tables within my database to return some search results. But as I delved further into designing the database architecture in the early planning stages, I found that my original (somewhat closed minded) approach wouldn't be flexible nor scalable enough to search and extract all the information I required.

From what I have researched, the general consensus is to either use SQL Full Text Search or Lucene.NET. Many have favoured the use of Lucene due to its richer querying language and generally more flexible since you have the ability to write a search index tailored to your project. From what I gather, Lucene can work with any type of text data. For example, you not only can index rows in your database but there are also solutions to support indexing physical files in your application. Neat!

I have written some basic code (below) with a couple methods to get started in creating a search index and carrying out a multi-query search across your whole index. You would further enhance this code to only carry out a full index once all required records have been added. Most implementations of Lucene would use incremental indexing, where documents already in the index are just updated individually, rather than deleting the whole index and building a new one every time. I plan to hook up and optimise my Lucene code into a service that would be scheduled to carry out an incremental index every midnight.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Lucene;
using Lucene.Net;
using Lucene.Net.Store;
using Lucene.Net.Analysis;
using Lucene.Net.Analysis.Standard;
using Lucene.Net.Index;
using Lucene.Net.Documents;
using Lucene.Net.QueryParsers;
using Lucene.Net.Search;
using System.Configuration; 

namespace MES.DataManager.Search
    public class Lucene
        public static void IndexSite()
                //The file location of the index
                string indexLocation = @ConfigurationManager.AppSettings["SearchIndexPath"];

                Directory searchDirectory = null;

                if (System.IO.Directory.Exists(indexLocation))
                    searchDirectory = FSDirectory.GetDirectory(indexLocation, false);
                    searchDirectory = FSDirectory.GetDirectory(indexLocation, true); 

                //Create an analyzer to process the text
                Analyzer searchAnalyser = new StandardAnalyzer(); 

                //Create the index writer with the directory and analyzer.
                IndexWriter indexWriter = new IndexWriter(searchDirectory, searchAnalyser, true);

                //Iterate through Article table and populate the index
                foreach (Article a in ArticleBLL.GetArticleDetails())
                    Document doc = new Document();

                    doc.Add(new Field("id", a.ID.ToString(), Field.Store.YES, Field.Index.UN_TOKENIZED, Field.TermVector.YES));
                    doc.Add(new Field("title", a.Title, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));
                    doc.Add(new Field("articletype", a.Type.TypeName, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES)); 

                    if (!String.IsNullOrEmpty(a.Summary))
                        doc.Add(new Field("summary", a.Summary, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));                

                    if (!String.IsNullOrEmpty(a.ByLineShort))
                        doc.Add(new Field("bylineshort", a.ByLineShort, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));                    

                    if (!String.IsNullOrEmpty(a.ByLineLong))
                        doc.Add(new Field("bylinelong", a.ByLineLong, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));                   

                    if (!String.IsNullOrEmpty(a.BasicWords))
                        doc.Add(new Field("basicwords", a.BasicWords, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));                   

                    if (!String.IsNullOrEmpty(a.MediumWords))
                        doc.Add(new Field("mediumwords", a.MediumWords, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));                   

                    if (!String.IsNullOrEmpty(a.LongWords))
                        doc.Add(new Field("longwords", a.LongWords, Field.Store.YES, Field.Index.TOKENIZED, Field.TermVector.YES));  

                    //Write the document to the index

                //Optimize and close the writer

        public static List<CoreArticleDetail> SearchArticles(string searchTerm)
            Analyzer analyzer = new StandardAnalyzer(); 

            //Search by multiple fields
            MultiFieldQueryParser parser = new MultiFieldQueryParser(
                                                                new string[]

            Query query = parser.Parse(searchTerm); 

            //Create an index searcher that will perform the search
            IndexSearcher searcher = new IndexSearcher(@ConfigurationManager.AppSettings["SearchIndexPath"]); 

            //Execute the query
            Hits hits = searcher.Search(query);

            List<int> articleIDs = new List<int>(); 

            //Iterate through index and return all article id’s
            for (int i = 0; i < hits.Length(); i++)
                Document doc = hits.Doc(i);


            return ArticleBLL.GetArticleSearchInformation(articleIDs);


As you can see, my example allows you to carry out a search across as many of your fields as you require which I am sure you will find useful. It took a lot of research to find out how to carry out a multi query search. Majority of the examples I found over the internet showed you how to search only one field.

The main advantage I can see straight away from using Lucene is that since the search data is held on disk, there is hardly any need to query the database. The only downside I can see is problems being caused by the possibility a corrupt index.

For more information on using Lucene, here are a couple of links that you may find useful to get started (I know I did):

Watermarking Images On The Fly Using ASP.NET

Watermarking and general image manipulation within the .NET Framework has become quite an easy thing to carry out thanks to the features provided by the System.Drawing namespace. The System.Drawing namespace contains types to help you with…well…drawing and rendering images. I will not be covering the basic use of the System.Drawing class. But feel free to carry out a Google.

My example consists of using a .NET (aspx) page and a Generic Handler (ashx). The .NET page will allow me to select an image, add a logo to the top left and some text. The Generic Handler will contain all the magic needed to manipulate the image based on selections made within the .NET page. The screenshot (below) shows my basic program in action.

Image Watermarking

Firstly, let me start off by showing you the code for the Generic Handler.


using System;
using System.Web;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;

public class ImageRenderJpeg : IHttpHandler
    public void ProcessRequest(HttpContext context)
        context.Response.ContentType = "image/jpeg";

        //Retrieve image details
        string imageUrl = context.Request.QueryString["ImageUrl"].ToString();
        string imageComment = context.Request.QueryString["ImageComment"].ToString();
        string imageIconUrl = context.Request.QueryString["Icon"].ToString();

        if (!String.IsNullOrEmpty(imageUrl))
            //Get the location of the image
            Image imagePhoto = Image.FromFile(imageUrl);

            // Get dimensions of image
            int imageHeight = imagePhoto.Height;
            int imageWidth = imagePhoto.Width;

            //Create a new Bitmap
            Bitmap oBitmap = new Bitmap(imageWidth, imageHeight, PixelFormat.Format24bppRgb);

            //Load Background Graphic from Image
            Graphics oGraphics = Graphics.FromImage(oBitmap);
            oGraphics.SmoothingMode = SmoothingMode.HighQuality;
            oGraphics.DrawImage(imagePhoto, new Rectangle(0, 0, imageWidth, imageHeight), 0, 0, imageWidth, imageHeight, GraphicsUnit.Pixel);

            //Layer 1: Add an Image Logo to the top left
            if (!String.IsNullOrEmpty(imageIconUrl))
                Image imageIcon = Image.FromFile(imageIconUrl);
                oGraphics.DrawImage(imageIcon, new Rectangle(5, 5, 124, 48), 0, 0, imageIcon.Width, imageIcon.Height, GraphicsUnit.Pixel);

            //Layer 2: Add Comment
            if (!String.IsNullOrEmpty(imageComment))
                Font commentFont = new Font("Arial", 14, FontStyle.Regular); //Font Style
                StringFormat commentFormat = new StringFormat();
                commentFormat.Alignment = StringAlignment.Near; //Align text in left of layer

                SolidBrush commentBrush = new SolidBrush(Color.Black); //Font Colour

                oGraphics.FillRectangle(Brushes.Beige, 5, imageHeight - 55, imageWidth - 15, 50); //Create a rectangle with white background
                oGraphics.DrawString(imageComment, commentFont, commentBrush, new Rectangle(5, imageHeight - 55, imageWidth - 15, 50), commentFormat); //Add comment text inside rectangle
            //Layer 3: Add Copyright watermark
            Font watermarkFont = new Font("Arial", 40, FontStyle.Bold); //Font Style
            SolidBrush semiTransBrush = new SolidBrush(Color.LightGray); //Font Colour
            StringFormat watermarkFormat = new StringFormat();
            watermarkFormat.Alignment = StringAlignment.Center; //Align text in center of image

                new PointF(imageWidth / 2, imageHeight / 2), watermarkFormat);

            //Dispose of graphic objects

            //Output image
            oBitmap.Save(context.Response.OutputStream, ImageFormat.Jpeg);


    public bool IsReusable
            return false;

You can see that I am manipulating my image based on the query string parameters I pass from my .NET page into my Generic Handler. Hopefully, my code is commented well enough to explain the general overview on what is going on.

The following code displays how my aspx page parses all the parameters needed to generate an image on the page:


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "">
<html xmlns="">
<head runat="server">
    <title>Image Generator</title>
    <form id="form1" runat="server">
        Select an Image:
        <asp:DropDownList ID="ddlImage" runat="server">
            <asp:ListItem Text="*" Value="*">Select Image</asp:ListItem>
            <asp:ListItem Text="The Tumbler" Value="C:\Users\Surinder\Documents\Visual Studio 2010\WebSites\ImageCreator\Images\batmobile_Tumbler.jpg"></asp:ListItem>
            <asp:ListItem Text="Audi TT" Value="C:\Users\Surinder\Documents\Visual Studio 2010\WebSites\ImageCreator\Images\new-audi-tt-coupe.jpg"></asp:ListItem>
            <asp:ListItem Text="Volvo Concept" Value="C:\Users\Surinder\Documents\Visual Studio 2010\WebSites\ImageCreator\Images\volvo-s60-concept-interior1.jpg"></asp:ListItem>
        <br />
        <br />
        Add Logo: <asp:TextBox ID="txtImage" runat="server"></asp:TextBox>
        <br />
        <br />
        Add a comment:
        <asp:TextBox ID="txtComment" runat="server" TextMode="MultiLine" Width="500" Height="50"></asp:TextBox>
        <br />
        <br />
        <asp:Button ID="btnCreateImage" Text="Create Image" runat="server" onclick="btnCreateImage_Click" />
        <br />
        <br />
        <img id="imgRender" alt="Image Render" title="Image Render" runat="server" />        


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class _Default : System.Web.UI.Page 
    protected void Page_Load(object sender, EventArgs e)
        if (ddlImage.SelectedValue == "*")
            imgRender.Visible = false;
            imgRender.Visible = true;

    protected void btnCreateImage_Click(object sender, EventArgs e)
        if (ddlImage.SelectedValue != "*")
            //The Image source will be pointed to our Generic Handler to display the image.
            imgRender.Src = String.Format("ImageRenderJpeg.ashx?ImageUrl={0}&ImageComment={1}&Icon={2}", ddlImage.SelectedValue, txtComment.Text, txtImage.Text);

Cannot Delete File: The file is in use by another program or user

Arrrgh!!! Microsoft has to be the yearly winner for the “Most Stupid Error Message” award for displaying the most excruciatingly annoying error messages in any of their products. The main reason for their annoyance is because majority of the error messages tells you something is wrong, but not the source of the error. A good example of this is: “Cannot delete file. This file is in use by another program or user”.

I am not expecting the error message to tell me the exact cause of the error but at least some meaningful information on what could be the issue. Anyway, back to the error at hand…

I found a really great bit of free software that fixes the problem. Its called Unlocker and you can download it here. It allows you to see exactly what processes are currently using the file you are trying to delete, edit or move. What’s even better is that Unlocker can kill the processes within its UI.

Making Calculations In LINQ

I am currently working on an ASP.NET 4.0  e-commerce site using Entity Framework alongside LINQ. I came across a small issue when I needed to carry out some calculations based on product pricing and the discounts that would need to be applied based on a specific customers allowance.

You maybe thinking, what’s the issue? Well I wanted to be able to make the calculations within my LINQ query since both product pricing and customer discount amounts are stored in the database. So initially wrote the following code:

using (MyEntities myContext = new MyEntities())
    int productPrice = (from p in myContext.Products
                        where p.ProductID == 1
                        select p.Price).SingleOrDefault(); 
    int customerDiscount = (from cd in myContext.CustomerDiscounts
                            where cd.CustomerID == 15
                            select cd.Discount).SingleOrDefault(); 
    int productDiscountedPrice = productPrice - ((productPrice * customerDiscount) / 100);

As you can see from my code above, I had to write two separate LINQ queries in order to get the values I wanted and then base my calculations on those values. But I was determined to carry out my calculations in one query. Luckily, LINQ has has a really cool keyword that I totally missed. It’s the “let” keyword which allows you to declare a variable and assign it a calculated value.

using (MyEntities myContext = new MyEntities())
    int productDiscount = (from cd in myContext.CustomerDiscounts
                           join p in myContext.Products on cd.ProductID equals cd.ProductID
                           where p.ProductID == 1 && cd.CustomerID == 15
                           let discountAmount = p.Price - ((p.Price * cd.Discount) / 100)
                           select discountAmount).SingleOrDefault();

Since my database schema allowed me to join my “CustomerDiscount” and “Products” table, I was able to join the two tables and retrieve values I required through one query.

Handling Unsupported Internet Explorer 6 Users

Web browsers have come a long way since the days of Internet Explorer 6 release back in 2001. You would think 9 years on we would have all dumped this piece of software in the garbage heap by now. Alas, we still have users to this very date who still use IE6 either due to personal preference or by force (company IT policies).

As everyone knows, developing a site to be compliant with main stream browsers in addition to carrying out additional fixes to fit in with the slim 6.7% of global users can be a real pain. So instead of trying to fit your site around the small number of IE6 users, why not just knock some common sense into them and notify them to upgrade.

Thankfully, there is a really easy and polite way to do this. Go to and download the JavaScript file and embed the following code to your webpage…

<!--[if lte IE 6]>
    <script src="js/ie6/warning.js"></script>

…which outputs the following result:

IE6 Upgrade Warning

As great as this idea is I don’t see many web developers or web agencies implementing this on the sites they create unless really needed. Nevertheless, its step in the right direction to hopefully put a final nail into that IE6 coffin!