RSS

Batch files(*.bat)

Lately, I’ve been creating quite a few batch files for some daily tasks. So, I thought it’s good idea to put some useful ones online so that I don’t loose them.

Batch file to restart SMTP server(RestartSMTP.bat)


@ECHO OFF
NET STOP smtpsvc
NET START smtpsvc

Batch file to restart SharePoint Time service(RestartTimerService.bat)


@ECHO OFF
NET STOP "SharePoint 2010 Timer"
NET START "SharePoint 2010 Timer"

Batch file to restart SQL Server(RestartSQLServer.bat)


@ECHO OFF

REM SQL Server agent is only needed to execute scheduled tasks on the SQL server. Generally not needed on Dev machines.

REM Display Name: "SQL Server Agent (MSSQLSERVER)"
REM NET STOP "SQLSERVERAGENT"

REM Display Name: "SQL Server (MSSQLSERVER)"
NET STOP "MSSQLSERVER"

NET STOP "SQL Server Integration Services 10.0"

REM Display Name: "SQL Server Analysis Services (MSSQLSERVER)"
NET STOP "MSSQLServerOLAPService"

REM Display Name: "SQL Server Reporting Services (MSSQLSERVER)"
NET STOP "ReportServer"
PAUSE

NET START "MSSQLSERVER" 
REM NET START "SQLSERVERAGENT" 
NET START "SQL Server Integration Services 10.0"
NET START "MSSQLServerOLAPService" 
NET START "ReportServer" 

PAUSE

NOTE: NET START/STOP commands work with both Service name and Display name of a Windows service. To get Service name/Display name of a windows service, go to Services(type services.msc in Run prompt) and go to properties of the service you’re looking for.

Batch file to open a text document(OpenTxt.bat)(Now this might look silly, but you never know. Just throwing this here since I have it. I usually use this to check some log files and I got fed up with opening the same folder multiple times)


Start notepad "C:\wsp\awp\DeploymentLog.txt"
exit

Batch file to delete files of a specific type(DeleteFile.bat)(this can be used for any file types)


REM This does not do recursive delete
del "C:\MyFolder\*.txt"

Batch file to get a list of all w3wp processes with their ids(GetW3WPs.bat)

@ECHO OFF

cd "C:\Windows\System32\inetsrv\"
appcmd list wp
PAUSE

Batch file to get information of a specific w3wp process(GetW3WP.bat)

%windir%\system32\inetsrv\appcmd.exe list wps /apppool.name:"MyApplicationPoolName"
PAUSE

Batch file to reset clipboard in a virtual machine(Virtual box only)(VBox ClipboardRestart.bat)(If you use virtual box frequently then I’m pretty sure you might have run into this at least once i.e., copy/paste doesn’t work between guest and host machines, and changing the virtual box settings don’t help either.)

@ECHO off 
ECHO "Stopping VBoxTray..."
TSKILL "VBoxTray" /a

NET STOP VBoxService
NET START VBoxService

ECHO "Starting VBoxTray..."
START C:\WINDOWS\system32\VBoxTray.exe

ECHO "Bidirectional clipboard is now enabled."

PAUSE
EXIT

Batch file to replace dll in GAC(ReGAC.bat)

@ECHO OFF
cd C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools

ECHO Replacing MyProject.dll
ECHO ----------------------
ECHO.
gacutil.exe -if "C:\MyWorkspace\MyProject\bin\Debug\MyProject.dll"
%systemroot%\system32\inetsrv\appcmd recycle apppool "MyApplicationPoolName"
ECHO.

PAUSE

Hopefully, these bats will help someone. I will try to update this post if I find any other useful bat files.

 
Leave a comment

Posted by on October 30, 2015 in Uncategorized

 

Tags: , , , , ,

Adding a custom icon to a feature in SharePoint 2010

Feature is a very common word in SharePoint. It is used to deploy Web Parts, Master Pages, Lists, what not it is used for deploying almost everything in SharePoint. Well….I am not writing this post to explain what a Feature is in SharePoint. Instead I want to write up about how we can add a custom icon to a SharePoint feature. If you navigate to “/_layouts/ManageFeatures.aspx?Scope=Site” page in your site collection, you will find a list of site collection level features, and also a small icon beside each feature.

FeatureWithoutIcon

Test Feature is a simple site collection level feature that I created. My goal is to change the image beside this feature. I used an existing image in “Images” folder in 14 hive(14-hive/TEMPLATE/IMAGES/EVENTS.GIF). Now, open up the Feature Manifest for the feature(Test Feature) in visual studio. In the Manifest tab, click “Edit Options” which will show an xml editor where we can edit the Feature’s xml (I am using Visual Studio 2012). And the xml will look similar to the xml shown below:


<Feature xmlns="http://schemas.microsoft.com/sharepoint/">
</Feature>

Note: This is a brand new feature with nothing added into it. This xml will be different depending on the feature’s content.

Now, to get the image add the attributes “ImageUrl” and “ImageUrlAltText”(Optional) to the “<Feature>” xml node. So, the changed Feature’s xml will be as follows:


<Feature xmlns="http://schemas.microsoft.com/sharepoint/" ImageUrl="events.gif" ImageUrlAltText="My New Feature">
</Feature>

Deploy your wsp and go to the ManageFeatures.aspx page in your site collection. Your feature will appear as shown below with a new image beside it.

FeatureWithIcon

That’s it. That’s all we have to do to add a custom icon to our feature in SharePoint.

Things to note:

1. Attributes ImageUrl and ImageUrlAltText are case sensitive.

2. ImageUrlAltText is an optional attribute used to specify alternate text for the custom icon

3. ImageUrl takes a URL relative to “/_layouts/IMAGES” directory in 14-hive

 
3 Comments

Posted by on March 10, 2013 in SharePoint

 

Tags: , , , , ,

The object has been updated by another user since it was last fetched.

Sometimes when we are updating Content Types programmatically, we might come across the following exception:

“The object has been updated by another user since it was last fetched”

I got this exception for the following code in my Feature Receiver’s FeatureActivated method:


private SPContentType EnsureContentType(SPWeb web, string contentTypeName)
{
    SPContentType contentType = web.ContentTypes[contentTypeName];
    if (contentType == null)
    {
        SPContentType parentContentType = web.ContentTypes[SPBuiltInContentTypeId.Item];
        contentType = new SPContentType(parentContentType, web.ContentTypes, contentTypeName);
        contentType = web.ContentTypes.Add(contentType);
    }

    contentType.FieldLinks[SPBuiltInFieldId.Title].Hidden = true;
    contentType.Description = contentTypeName;
    contentType.Group = "Test";
    contentType.Update();
}

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPSite site = properties.Feature.Parent as SPSite;
    using (SPWeb web = site.OpenWeb())
    {
        SPContentType contentType = EnsureContentType(web, "My Content Type");
        Guid guid = "CBE1FD3E-351B-43B8-94A6-33868300FA6E"; //Some valid Guid
        if (contentType.FieldLinks[guid] != null)
        {
            contentType.FieldLinks.Delete(guid);
            contentType.Update(true); //This is the line that throws exception
        }
    }
}

This exception occurs randomly. The same piece of code is working fine in a different place. The only way that I found to fix this problem is to instantiate new SPSite and SPWeb objects, instead of using SPContext.Current.Web or SPWeb from properties object.

So, I changed my code as shown below to get rid of the exception.

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPSite site = properties.Feature.Parent as SPSite;
    using (SPWeb web = site.OpenWeb())
    {
        SPContentType contentType = EnsureContentType(web, "My Content Type");
        Guid guid = "CBE1FD3E-351B-43B8-94A6-33868300FA6E"; //Some valid Guid
        //Instantiating new SPSite and SPWeb objects
        using (SPSite newSite = new SPSite(web.Site.ID))
        {
            using (SPWeb newWeb = newSite.OpenWeb(web.ID))
            {
                contentType = newWeb.ContentTypes[contentType.Id];
                if (contentType.FieldLinks[guid] != null)
                {
                    contentType.FieldLinks.Delete(guid);
                    contentType.Update(true); //No more exceptions
                }
            }
        }
    }
}

Note: You might also face this problem if you are creating/updating Content Types using xml files. There is a Version attribute in your Elements.xml file that needs to be removed to fix the same problem. For more information, you can refer to this blog where the problem is explained in detail.

That’s it guys……a small post on a problem that can be a huge pain.

 
4 Comments

Posted by on January 12, 2013 in SharePoint

 

Tags: , , , ,

Creating a custom Error page in SharePoint 2010……..and, yeah I know, along with Correlation ID.

Let’s talk about creating a custom error page in SharePoint 2010. I bet anyone who’s using SharePoint should have seen the below screen by now.

Error

As the error says, this page shows up when some unexpected/unhandled error occurs in the page.

Ok, now showing this screen to a user on a SharePoint site that is completely customized(Different look and feel) to the user’s needs is……..probably not good a idea. Instead, it’d be better if we show an Error page that matches the branding of the site. So, let’s replace SharePoint’s Out-of-the-box Error page with our Custom Error page.

An Error page can be overridden only at Web Application level. Add a new feature to your SharePoint project in Visual Studio and make sure that the feature is scoped at Web Application level. Now, add a Feature Receiver and write the following code:


public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    string CustomErrorPage = "/_layouts/CustomError.aspx";
    SPWebApplication webApp = (SPWebApplication)properties.Feature.Parent;
    if (!webApp.UpdateMappedPage(SPWebApplication.SPCustomPage.Error, CustomErrorPage))
    {
        throw new System.ApplicationException("Cannot replace the current error page.");
    }

    webApp.Update(true);
}

To override some Out-of-the-box pages like Error.aspx, AccessDenied.aspx, etc., we have to call UpdateMappedPage() method on SPWebApplication object. This method returns true if the Error page is replaced successfully. UpdateMappedPage() method takes SPCustomPage enum which specifies the Application Page to replace.

SPCustomPage enum is defined as


// Summary:
// Specifies the type of application page to replace.
public enum SPCustomPage
{
    // Summary:
    // Specifies that no page will be replaced with a custom page.
    None = 0,
    //
    // Summary:
    // Specifies that the AccessDenied.aspx page will be replaced with a custom
    // page.
    AccessDenied = 1,
    //
    // Summary:
    // Specifies that the Confirmation.aspx page will be replaced with a custom
    // page.
    Confirmation = 2,
    //
    // Summary:
    // Specifies that the Error.aspx page will be replaced with a custom page.
    Error = 3,
    //
    // Summary:
    // Specifies that the Login.aspx page will be replaced with a custom page.
    Login = 4,
    //
    // Summary:
    // Specifies that the ReqAcc.aspx page will be replaced with a custom page.
    RequestAccess = 5,
    //
    // Summary:
    // Specifies that the Signout.aspx page will be replaced with a custom page.
    Signout = 6,
    //
    // Summary:
    // Specifies that the WebDeleted.aspx page will be replaced with a custom page.
    WebDeleted = 7,
}

Reference: http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.administration.spwebapplication.spcustompage.aspx

Now, when the above feature is activated, your Custom Error page will replace SharePoint’s Out-of-the-box Error page. And to revert the Error page back to Out-of-the-box Error page, add the following code in FeatureReceiver’s FeatureDeactivating event as follows:


public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
    if (!webApp.UpdateMappedPage(SPWebApplication.SPCustomPage.Error, null))
    {
        throw new System.ApplicationException("Cannot replace the current error page.");
    }

    webApp.Update(true);
}

So far, we’ve seen how to set up the Custom Error page. Now let’s design our Custom Error page. We can simply create a normal aspx page and put some user friendly message in it. But, the main thing to note is the Correlation ID. Correlation ID is helpful in resolving the problem, as we can use this Correlation ID to get what the exact error is from the Log files(Usually in 14-hive/Logs folder). Our aim is to show Correlation ID in our Custom Error page. Instead of starting form the scratch, we can modify the existing SharePoint’s error page to suite our needs.

Add SharePoint’s error page(this file can be found in 14-hive/TEMPLATE/Layouts directory and file name is error.aspx) to the Layouts folder in your Visual Studio project. Rename the error.aspx to CustomError.aspx (or which ever name you specified in the FeatureActivated event of your WebApplication scoped feature, and make sure CustomError.aspx page goes to 14-hive/TEMPLATE/Layouts directory), and we don’t need a code-behind file for our CustomError.aspx page(as we can use the code-behind file of SharePoint’s error page). Now open up the CustomError.aspx and find the Label tag with ID “RequestGuidText”. This is the Label which will be set by SharePoint as the current Correlation ID. And also, the Label tag with ID “DateTimeText” will be set as the Date and Time the error occured. All you have to do is  add your custom html to this page, rearrange these Label tags into corresponding div/p tags based on your html and hide unwanted server tags in the page.

My customized error page looks like:

Error1

And my CustomError.aspx’s html is as follows(please don’t mind about the design of the page, I just made it up for the sake of this post….🙂 )


<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%> <%@ Page Language="C#" Inherits="Microsoft.SharePoint.ApplicationPages.ErrorPage" MasterPageFile="~/_layouts/simple.master" %> <%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %> <%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %> <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %> <%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ID="Content1" ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">
<SharePoint:EncodedLiteral ID="EncodedLiteral1" runat="server" text="<%$Resources:wss,error_pagetitle%>" EncodeMethod='HtmlEncode'/>
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">
<span id="errorPageTitleSpan" tabindex="0"><SharePoint:EncodedLiteral ID="EncodedLiteral2" runat="server" text="<%$Resources:wss,error_pagetitle%>" EncodeMethod='HtmlEncode'/></span>
</asp:Content>

<asp:Content ID="Content3" contentplaceholderid="PlaceHolderAdditionalPageHead" runat="server">
  <meta name="Robots" content="NOINDEX " />
  <meta name="SharePointError" content="0" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="Copyright" content="Copyright KPMG 2012. All Rights Reserved.">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
</asp:Content>

<asp:Content ID="Content4" ContentPlaceHolderId="PlaceHolderMain" runat="server">

<style type="text/css">
body {font: 12px Helvetica, Arial, sans-serif;}
body {color: #222;}
.wrapper { margin: 0 auto; width: 1044px; }
.wrapper1 {
    padding-left: 30px;
    height: 680px;
    position: absolute;
    left: 414.5px;
    top: -58px;
}
.div_bg {
    position: absolute;
    left: -20px;
    bottom: 140px;
    width: 700px;
    height: 400px;
    opacity: 0.8;
    filter:alpha(opacity=80);
    background: none repeat scroll 0 0 #4DB849;
    border: 6px ridge blue;
}
.content {
    bottom: 140px;
    height: 374px;
    top: 130px;
    left: -20px;
    position: absolute;
    width: 700px;
}
.innerDiv {
    color:blue;
    font-size: 1.6em;
    line-height: 1.5em;
    padding: 10px 40px 40px;
    margin-left: 10px;
    border: 4px outset #FCD209;
}
.ulAlign {
    list-style: none;
    font-size: .9em;
    margin-left: -22px;
    padding-top: 10px;
}

 /*To hide unwanted stuff from Out-of-the-box error page*/
 body #s4-simple-card { margin: 0px; width: 0px; border-style: none; }
 #s4-simple-card-top { display: none; }
 body #s4-simple-content { margin-left: 0px; }
 .s4-simple-iconcont { display: none; }
 #errorPageTitleSpan { display: none; }
 #s4-simple-gobackcont { display: none; }

</style>

<script type="text/javascript">
 /* Add any javascript code as needed. */
</script>

<div class="wrapper">
  <div class="wrapper1">
    <div class="div_bg"></div>
    <div class="content">
      <h1 style="font-size: 4.5em; color: white; padding: 10px 0 10px 40px; margin-left: 10px; border: 2px solid yellow; outline: 0 none; vertical-align: baseline;">Error!!</h1>
      <div class="innerDiv" style="">An unexpected error has occurred. Please report this problem to your <a style="color:blue; text-decoration: underline !important" href='mailto: admin@test.com'>Administrator</a><br>
        <ul class='ulAlign'>
          <li> <b>-</b> <span style="color:white"><asp:Label ID="RequestGuidText" Runat="server" /></span></li>
          <li> <b>-</b> <span style="color:white"><asp:Label ID="DateTimeText" Runat="server" /></span></li>
        </ul>
      </div>
    </div>
  </div>
</div>

<!--I marked the below content as Visible=false in my aspx page, as don't need it-->
<SharePoint:UIVersionedContent ID="UIVersionedContent1" UIVersion="3" runat="server">
 <ContentTemplate>
 <table width="100%" border="0" class="ms-titleareaframe" cellpadding="0">
 <tr>
 <td valign="top" width="100%" style="padding-top: 10px" class="ms-descriptiontext">
 </ContentTemplate>
</SharePoint:UIVersionedContent>
 <SharePoint:FormattedString id="LabelMessage" EncodeMethod="HtmlEncodeAllowSimpleTextFormatting" runat="server" Visible="false">
 <asp:HyperLink id="LinkContainedInMessage" runat="server"/>
 </SharePoint:FormattedString>
 <p style="display:none">
 <span class="ms-descriptiontext">
 <asp:HyperLink id="AdditionalHelpLink" Visible="false" runat="server"/>
 </span>
 </p>
 <p style="display:none">
 <span class="ms-descriptiontext"></span>
 </p>
<SharePoint:UIVersionedContent ID="UIVersionedContent2" UIVersion="3" runat="server">
 <ContentTemplate>
 </td>
 </tr>
 </table>
 </ContentTemplate>
</SharePoint:UIVersionedContent>
<script type="text/javascript" language="JavaScript">
 // <![CDATA[
 function ULSvam() { var o = new Object; o.ULSTeamName = "Microsoft SharePoint Foundation"; o.ULSFileName = "error.aspx"; return o; }
 var gearPage = document.getElementById('GearPage');
 if (null != gearPage) {
 gearPage.parentNode.removeChild(gearPage);
 document.title = "<SharePoint:EncodedLiteral runat='server' text='<%$Resources:wss,error_pagetitle%>' EncodeMethod='HtmlEncode'/>";
 }
 function _spBodyOnLoad() {
 ULSvam:;
 var intialFocus = document.getElementById("errorPageTitleSpan");
 try {
 intialFocus.focus();
 }
 catch (ex) {
 }
 }
 // ]]>
</script>

</asp:Content>

That’s it guys, we have our custom error page. You can also use Javascript in this page. Just put your code in the script tags(and you can add more script tags like one for jquery library, etc., as needed).

Hope this helps some one.

 
4 Comments

Posted by on December 8, 2012 in .NET, SharePoint

 

Tags: , , , , , ,

Find all the places where a Site Column in used in a Site Collection.

I was trying to delete a Site Column in my Site Collection and when I went to the Site Settings -> Site Columns page and delete the Site Column, I got this error message.

The reason for this message is that the Site Column is being used by some Content Types and Lists. So, first I had to delete this site column in all the Content Types and Lists that are using it. I didn’t know how many Content Types/Lists are using this site column and I had no intention of going through each Content Type and Lists in my site manually. After some quick googling, I found this guy’s blog post which exactly does what I was looking for. So, I wrote a console app to get the information I need, which in this case is a list of ContentTypes and Lists that are using the Site Column. Below is the code that I wrote:

class Program
{
    static void Main(string[] args)
    {
        List<string> webContentTypes = new List<string>();
        List<string> listContentTypes = new List<string>();
        List<string> lists = new List<string>();
        List<string> failedListIds = new List<string>();
        using(SPSite site = new SPSite("http://abc.xyz.com/test"))
        {
            SPField field = site.RootWeb.AvailableFields[SPBuiltInFieldId.AssignedTo];

            Console.WriteLine("The \"{0}\" field is used in: \n", field.Title);

            ICollection<SPFieldTemplateUsage> collection = field.ListsFieldUsedIn();
            foreach( SPFieldTemplateUsage usage in collection)
            {
                SPWeb web = site.AllWebs[usage.WebID];
                SPList list = null;
                try
                {
                    list = web.Lists[usage.ListID];
                }
                catch{}

                if(list == null)
                    failedListIds.Add(string.Format("Failed List Id: \"{0}\"", usage.ListID));
                else
                {
                    if(list.ContentTypesEnabled)
                    {
                        SPContentTypeCollection contentTypes = list.ContentTypes;

                        foreach (SPContentType contentType in contentTypes)
                        {
                            listContentTypes.Add(string.Format("\"{0}\" Content Type in \"{1}\" list", contentType.Name, list.Title));
                        }
                    }

                    if(list.Fields.ContainsField(field.InternalName))
                    {
                        lists.Add(string.Format("\"{0}\" list in \"{1}\"", list.Title, web.Title));
                    }
                }

                web.Dispose();
            }

            SPWeb web1 = site.OpenWeb();
            foreach (SPContentType contentType in web1.ContentTypes)
            {
                if (contentType.Fields.ContainsField(field.InternalName))
                {
                    webContentTypes.Add(string.Format("\"{0}\" Content Type in \"{1}\"", contentType.Name, web1.Title));
                }
            }
        }

        Console.WriteLine("Lists\n");
        lists.ForEach(i => Console.WriteLine("{0}\t", i));
        Console.WriteLine("\n****************\n");
        Console.WriteLine("Web Content Types\n");
        webContentTypes.ForEach(i => Console.WriteLine("{0}\t", i));
        Console.WriteLine("\n****************\n");
        Console.WriteLine("List Content Types\n");
        listContentTypes.ForEach(i => Console.WriteLine("{0}\t", i));
        Console.WriteLine("\n****************\n");
        Console.WriteLine("Failed List Ids\n");
        failedListIds.ForEach(i => Console.WriteLine("{0}\t", i));
    }
}

That’s it….This little piece of code made my life a little easier that day……..

 
1 Comment

Posted by on July 12, 2012 in SharePoint

 

Tags: , , , ,

Using jqGrid in ASP.NET/SharePoint

In this post, I will show how to use a jqGrid (This approach will work in an ASP.NET application or a SharePoint site). I will try to cover as many properties of jqGrid as I can.

To start off, I added references to the following files on my *.aspx page.

1. jquery-ui-1.8.18.custom.css

2. ui.jqgrid.css

3. jquery-1.7.1.min.js

4. jquery-ui-1.8.18.custom.min.js

5. grid.locale-en.js

6. jquery.jqGrid.min.js

7. json2.js

Files 1, 4 and the corresponding images files can be downloaded from the jQuery UI ThemeRoller site. And files  2, 5, 6 can be downloaded from the jqGrid site‘s download page(although I recommend downloading these files from a site that has a working example of jqGrid. The reason I suggest this method is, when I tried downloading these files from the jqGrid’s website by selecting the required modules, things worked fine for me initially, but later I reached a point where the jqGrid was not working for me, though there was no problem in my code. Then I replaced the jqGrid js file that I downloaded from the jqGrid website with a js file from a site that has a working example……Voila!!!…….it worked, without make any changes in my code. This happened to me twice, may be I was doing something wrong but just wanted to let you guys know of this case. This is just like one of those Visual Studio restart things….🙂 …just kidding). I will talk about file 7 at the end of this blog.

First let’s consider a normal jqGrid with some static data.

1. Add the following html code to your page

<table id="myGrid"></table>
<div id="pager"></div>

2. Add the following javascript code(in script tags) to your page (I will be referencing the below code section as “myJqCodeBlock” throughout the post)


var mydata = [

    { EID: "8", EmployeeName: "Name 1", EmployeeIndex: "10.00", EmployedDate: "2010-05-24", CurrentlyEmployed: true, SiteID: "a" },
    { EID: "67", EmployeeName: "Name 2", EmployeeIndex: "20.00", EmployedDate: "2010-05-25", CurrentlyEmployed: false, SiteID: "b" },
    { EID: "34", EmployeeName: "Name 3", EmployeeIndex: "30.00", EmployedDate: "2007-09-01", CurrentlyEmployed: true, SiteID: "c" },
    { EID: "14", EmployeeName: "Name 4", EmployeeIndex: "10.00", EmployedDate: "2007-10-04", CurrentlyEmployed: false, SiteID: "d" },
    { EID: "52", EmployeeName: "Name 5", EmployeeIndex: "20.00", EmployedDate: "2007-10-05", CurrentlyEmployed: true, SiteID: "e" },
    { EID: "6", EmployeeName: "Name 6", EmployeeIndex: "30.00", EmployedDate: "2007-09-06", CurrentlyEmployed: false, SiteID: "f" },
    { EID: "2", EmployeeName: "Name 7", EmployeeIndex: "10.00", EmployedDate: "2007-10-04", CurrentlyEmployed: true, SiteID: "g" }

    ];

$(function() {
     var grid = jQuery("#myGrid");
     grid.jqGrid({
         data: mydata,
         datatype: "local",
         height: 200,
         hidegrid: false,//hides the arrow button at the top right corner for collapsing the jqGrid
         rowNum: 10,
         rowList: [10, 20, 30],
         viewrecords: true,
         caption: "Employees",
         pager: "#pager",
         colNames: ['EID', 'Name', 'Index', 'Employed Date', 'Employed', 'Url'],
         colModel: [
             { name: 'EID', index: 'EID', width: 100, align: "center", sorttype: 'int' },
             { name: 'EmployeeName', index: 'EmployeeName', width: 100, align: "left" },
             { name: 'EmployeeIndex', index: 'EmployeeIndex', width: 100, align: "center" },
             { name: 'EmployedDate', index: 'EmployedDate', width: 120, align: "right", formatter: 'date', formatoptions: { newformat: 'm-d-Y' }, datefmt: 'm-d-Y' },
             { name: 'CurrentlyEmployed', index: 'CurrentlyEmployed', width: 100, align: "center", formatter: 'checkbox' },
             { name: 'SiteID', index: 'SiteID', width: 100, formatter: linkFormatter, sortable: false }
                   ]
     });
});

function linkFormatter(cellValue, options, rowdata, action) {
    return "<a href='https://praneethmoka.wordpress.com?rid=" + options.rowId + "' target='_blank'>Click here</a>";
}

In the above example, I tried to include different possible types of columns(int, float, boolean, string, date, URL) in the grid, just to show how jqGrid handles different datatypes.

So, here’s how the jqGrid looks so far.

jqGrid Example

jqGrid Example

Both sorting and paging automatically come Out-Of-The-Box for jqGrid. So, you need not go through all the struggle trying to set these up as you would in case of a Repeater or DataList.

You can also add search functionality to this jqGrid and this can be done in two ways, depending on your preference of UI.

Method 1:

Add the following line after line 34 in my “myJqCodeBlock”.


grid.jqGrid('navGrid', "#pager", { edit: false, add: false, del: false, search: true, refresh: true });

(Note: The second parameter(“#pager” in the above example) must be the id of the pager div tag in “#id” format. If you try to pass the pager jQuery object, it will blow up.)

After adding the above line, the jqGrid looks as follows:

See the new buttons added at the bottom left corner of the jqGrid. One button was added by setting the “search” property to true and the second button was added by setting the “refresh” property to true.

The “refresh/reload” button just reloads the jqGrid data.

Ok, now if you click the “search” button, a new popup(see the image below) will come up where you can setup your search criteria.

jqGrid Example

jqGrid Example

Here you can get a wide range of search options all Out-Of-The-Box. Ok, now let’s move on to the next method.

Method 2:

In the second method, instead of having a search popup for setting your seach criteria, we can create a filter toolbar that shows up below the jqGrid column headers as follows:

jqGrid Example

jqGrid Example

See the text boxes below each column heading, you can type in your text in these text boxes and as you type in your text, jqGrid content will be filtered automatically without any page refreshes.

To get the above Filter Toolbar, add the following code after line 34 in my “myJqCodeBlock”.


grid.jqGrid('filterToolbar', { stringResult: true, searchOnEnter: false, defaultSearch: "cn" });

Now, if you don’t like jqGrid filtering its content as and when you type your text, you can set the “searchOnEnter” property in the above code to true. Then jqGrid’s content will be filtered only after you press the “Enter” key on your keyboard.

And while you are playing with the Filter Toolbar, you might notice that the search is not case-sensitive. To make it case-sensitive, all you have to do is set the following property in the jqGrid properties.

Add the following property after line 23 in my “myJqCodeBlock”.


ignoreCase: true

If you have already noticed, the way filtering works is it looks for records with column having a value that CONTAINS your search text. If you want to change this filtering in a way that jqGrid looks only for records with column having an EXACT MATCH of your search text, all you have to do is change the “defaultSearch” property of the Filter Toolbar property to “eq”. (Check the code below)


grid.jqGrid('filterToolbar', { stringResult: true, searchOnEnter: false, defaultSearch: "eq" });

If you want to change the height of the Filter Toolbar, add the following code after setting the Filter Toolbar properties

var $toolbar = $("tr.ui-search-toolbar", grid[0].grid.hDiv);
$toolbar.height(30);

If you want to disable filtering for particular columns, say for columns ‘Employed’ and ‘Url’ in my case, we’ll just have to set the search property to false in corresponding column’s colModel property. So, the colModel section of the jqGrid would change as follows:


colModel: [
{ name: 'EID', index: 'EID', width: 100, align: "center", sorttype: 'int' },
{ name: 'EmployeeName', index: 'EmployeeName', width: 100, align: "left" },
{ name: 'EmployeeIndex', index: 'EmployeeIndex', width: 100, align: "center" },
{ name: 'EmployedDate', index: 'EmployedDate', width: 120, align: "right", formatter: 'date', formatoptions: {
newformat: 'm-d-Y' }, datefmt: 'm-d-Y' },
{ name: 'CurrentlyEmployed', index: 'CurrentlyEmployed', width: 100, align: "center", formatter: 'checkbox',
search: false },
{ name: 'SiteID', index: 'SiteID', width: 100, formatter: linkFormatter, sortable: false, search: false }
 ]

And the jqGrid will look as follows:

(Notice the difference marked in red highlighted section)

Next………sometimes, you might want to show/hide the toolbar at your will. In order to do this, I added a button to pager and I called the “toggleToolbar()” function in its onClick event. Add the following code after adding the Filter Toolbar. The code goes as follows:

grid.jqGrid('navButtonAdd', "#pager", { caption: "Search", title: "Search Toolbar", buttonicon: 'ui-icon-search',
    onClickButton: function () {
        grid[0].toggleToolbar();
    }
});

grid[0].toggleToolbar(); //To hide the Toolbar initially

The jqGrid will now have a button at the bottom left corner as shown below:

(Note: In order to use “navButtonAdd” method, a “navGrid” method should be called before calling “navButtonAdd”)

Instead of text boxes in the Filter Toolbar, we can also get a Dropdown list that has a list of unique values in a column. Now, let’s try changing the Textbox to Dropdown list for the “Name” column.

Add the following functions to your javascript code:


function buildSearchSelect(uniqueNames) {
  var values = ":All";
  $.each(uniqueNames, function (index) {
    values += ";" + this + ":" + this;
  });
  return values;
}

function getUniqueNames(uList, columnName) {
  var uniqueTexts = [], text, textsMap = {}, i;
  for (i = 0; i < uList.length; i++) {
    text = uList[i][columnName];
    if (text !== undefined && textsMap[text] === undefined) {
      textsMap[text] = true;
      uniqueTexts.push(text);
    }
  }
  return uniqueTexts;
}

function setSearchSelect(grid, uList, columnName, filterType) {
  grid.jqGrid('setColProp', columnName,
  {
    stype: 'select',
    searchoptions: {
    value: buildSearchSelect(getUniqueNames(uList, columnName)),
    sopt: [filterType],
    dataInit: function (elem) { $(elem).height(20); $(elem).css('margin-top',  '2px'); }
  }
  });
};

Then add the “loadComplete” event of the jqGrid after it’s colModel properties and call the “setSearchSelect()” by passing the required parameters as follows:

grid.jqGrid({
    data: mydata,
    datatype: "local",
    hidegrid: false,
    height: 200,
    rowNum: 10,
    rowList: [10, 20, 30],
    viewrecords: true,
    caption: "Employees",
    ignoreCase: true,
    pager: "#pager",
    colNames: ['EID', 'Name', 'Index', 'Employed Date', 'Employed', 'Url'],
    colModel: [
        { name: 'EID', index: 'EID', width: 100, align: "center", sorttype: 'int' },
        { name: 'EmployeeName', index: 'EmployeeName', width: 100, align: "left" },
        { name: 'EmployeeIndex', index: 'EmployeeIndex', width: 100, align: "center" },
        { name: 'EmployedDate', index: 'EmployedDate', width: 120, align: "right", formatter: 'date', formatoptions: { newformat: 'm-d-Y' }, datefmt: 'm-d-Y' },
        { name: 'CurrentlyEmployed', index: 'CurrentlyEmployed', width: 100, align: "center", formatter: 'checkbox', search: false },
        { name: 'SiteID', index: 'SiteID', width: 100, formatter: linkFormatter, sortable: false, search: false }
    ],
    loadComplete: function () {
        setSearchSelect(grid, mydata, 'EmployeeName', 'eq');
    }
});

And now the jqGrid turns out to be as:

Ok, now the search part of the jqGrid is over. Let’s move onto the next feature.

We can also hide columns in a jqGrid. We can either do this when we setup the colModel properties of the jqGrid or through a line of javascript code later based on some condition. To hide the “EID” column, add the “hidden” property in its colModel property.


{ name: 'EID', index: 'EID', width: 100, align: "center", sorttype: 'int', hidden: true }

Or we can say


grid.jqGrid('hideCol', grid.getGridParam("colModel")[0].name); // 0 indicates first column in this case

Sometime back when I was looking into some stuff related to jqGrid, I found an interesting functionality for jqGrid in some stackoverflow post, posted by some guy(Unfortunately I don’t have the link to that post).

This guy posted some code using which we can add a button in Column headers of the jqGrid. The below picture explains what I am talking about.

To add buttons in column headers, add the following javascript code after setting up the jqGrid.


grid.closest("div.ui-jqgrid-view").find("div.ui-jqgrid-hdiv table.ui-jqgrid-htable tr.ui-jqgrid-labels > th.ui-th-column > div.ui-jqgrid-sortable")
.each(function () {
  $('<button>').css({ float: "right", height: "17px" }).appendTo(this).button({
 icons: {
 primary: "ui-icon-wrench"
 },
 text: false
 }).click(function (e) {
 var idPrefix = "jqgh_" + grid[0].id + "_",
 thId = $(e.target).closest('div.ui-jqgrid-sortable')[0].id;
 // thId will be like "jqgh_list_name"
 if (thId.substr(0, idPrefix.length) === idPrefix) {
 alert('Clicked the button in column "' + thId.substr(idPrefix.length) + '"');
 return false;
 }
 });
});

Let’s change the data source of the jqGrid now. Sometimes, instead of static data we need to show data from code-behind in jqGrid. This can be done easily and I’ll show how this is done.

I created a custom C# class called “Manager”.


public class Manager
{
    public int EID { get; set; }
    public string EName { get; set; }
    public float EIndex { get; set; }
    public DateTime EmployedDate { get; set; }
    public bool CurrentlyEmployed { get; set; }
    public string SiteID { get; set; }
}

And in the code-behind file of my .aspx page I have the following method:

protected string BuildManagers()
{
    double d = 10.3;
    List managers = new List();
    bool temp = false;
    for (int i = 0; i < 10; i++)
    {
        managers.Add(new Manager
                        {
                            EID = i + 1,
                            EmployeeName = "Name " + i,
                            EmployeeIndex = Math.Round((d + i)/3, 3),
                            EmployedDate = DateTime.Now,
                            CurrentlyEmployed = temp,
                            SiteID = "a" + (i + 1)
                        });
        temp = !temp;
    }
    System.Web.Script.Serialization.JavaScriptSerializer oSerializer = new System.Web.Script.Serialization.JavaScriptSerializer();
    return oSerializer.Serialize(managers);
}

Then, all we have to do is to replace 1-11 in “myJqCodeBlock” with the following line

<%= var mydata = BuildManagers() %>;

That’s it, now you will be able to see the jqGrid with all the data from your code-behind method.

We can also add data dynamically to jqGrid:

for(var i = 0; i < mydata.length; i++)
{
    grid.jqGrid('addRowData', i + 1, mydata[i]);
}

Or:

grid.addJSONData(mydata);

“mydata” can be either be a static array declared in javascript(as we started off with) or it can be a serialized string returned from code-behind or it can be a ResponseText returned from an ajax call.

And the next functionality that I want to discuss is showing a jqGrid(sub grid) within a jqGrid. Let’s consider Manager -> Employee hierarchy for this case. I will show all the Managers in the Parent grid and all the Employees under a particular Manager in a sub grid(The data might not make sense, I just made it up to show the functionality of a sub grid).

To implement this functionality, instead of loading all the data at once on PageLoad(which might take a lot of time depending on the data size), we will load data(using Ajax and PageMethods in ASP.NET) as we need it.

Let’s start……..

Create the following C# classes:

Employee.cs

public class Employee
{
    public int EID { get; set; }
    public int MID { get; set; }
    public string EmployeeName { get; set; }
}

EmployeeManagement.cs (This must be a static class)


public static class EmployeeManagement
{
    public static List GetEmployeesByMID(int ManagerID)
    {
        List allEmployees = BuildEmployeesList();
        return allEmployees.Where(e => e.MID == ManagerID).ToList();
    }

    private static List BuildEmployeesList()
    {
        List employees = new List();

        employees.Add(new Employee
                          {
                              EID = 1,
                              EmployeeName = "Employee 1",
                              MID = 2
                          });

        employees.Add(new Employee
                          {
                              EID = 2,
                              EmployeeName = "Employee 2",
                              MID = 2
                          });

        employees.Add(new Employee
                          {
                              EID = 3,
                              EmployeeName = "Employee 3",
                              MID = 3
                          });

        return employees;
    }
}

Say, my aspx page is called Grid.aspx(This page has all the jqGrid references and code).
Now, in my code-behind file(Grid.aspx.cs), add the following method.

[System.Web.Services.WebMethod(EnableSession=false)]
public static string GetEmployees(int ManagerID)
{
    List employees = EmployeeManagement.GetEmployeesByMID(ManagerID);
    JavaScriptSerializer serializer = new JavaScriptSerializer();
    return serializer.Serialize(employees);
}

Note: GetEmployees() method must be a static method for it to be used as a WebMethod.
Now, to get the sub grid add the jqGrid code as follows:


var mydata = ;

$(function () {
    var grid = jQuery("#myGrid");
    grid.jqGrid({
    data: mydata,
    datatype: "local",
    hidegrid: false,
    height: 250,
    rowNum: 10,
    rowList: [10, 20, 30],
    viewrecords: true,
    caption: "Employees",
    ignoreCase: true,
    pager: "#pager",
    colNames: ['EID', 'Name', 'Index', 'Employed Date', 'Employed', 'Url'],
    colModel: [
        { name: 'EID', index: 'EID', width: 100, align: "center", sorttype: 'int' },
        { name: 'EmployeeName', index: 'EmployeeName', width: 100, align: "left" },
        { name: 'EmployeeIndex', index: 'EmployeeIndex', width: 100, align: "center" },
        { name: 'EmployedDate', index: 'EmployedDate', width: 120, align: "right", formatter: 'date', formatoptions: { newformat: 'm-d-Y' }, datefmt: 'm-d-Y' },
        { name: 'CurrentlyEmployed', index: 'CurrentlyEmployed', width: 100, align: "center", formatter: 'checkbox', search: false },
        { name: 'SiteID', index: 'SiteID', width: 100, formatter: linkFormatter, sortable: false, search: false }
              ],
    loadComplete: function () {
            setSearchSelect(grid, mydata, 'EmployeeName', 'eq');
        },
    jsonReader: {
        userdata: 'Data'
    },
    subGrid: true,
    subGridOptions: {
        "plusicon"  : "ui-icon-triangle-1-e",
        "minusicon" : "ui-icon-triangle-1-s",
        "openicon"  : "ui-icon-arrowreturn-1-e"
    },
    subGridRowExpanded: function(subgrid_id, row_id) {
        var subgrid_table_id, pager_id;
        subgrid_table_id = subgrid_id + "_t";
        pager_id = "p_" + subgrid_table_id;
        $("#" + subgrid_id).html("<table id='" + subgrid_table_id + "' class='scroll'></table><div id='" + pager_id + "' class='scroll'></div>");
        //To get the selected ManagerID
        curManagerID = grid.getGridParam('data')[row_id - 1]['EID'];

        var subGrid = $("#" + subgrid_table_id).jqGrid({
                          datatype: 'jsonstring',
                          colNames: ['Employee ID', 'Employee Name'],
                          colModel: [
                              { name: "EID", index: "EID", width: 100, key: true, align:"center", sorttype: 'int' },
                              { name: "EmployeeName", index: "EmployeeName", width: 100, align:"center" }
                          ],
                          autowidth: true,
                          height: '80px',
                          rowNum: 10,
                          rowList: [1, 5, 10, 15],
                          pager: pager_id,
                          caption: "Employees under this Manager",
                          hidegrid: false,
                          ignoreCase: true,
                          loadComplete: function() {       }
                      });

                      getData(subgrid_table_id, curManagerID);
    }
});

Lines 31-65 will set you up with the sub grid. getData() will be the method that loads data into the sub grid when it is opened. Add the following methods to finish up the sub grid code:

function getData(subgrid_table_id, managerID)
{
    var param = new Object();
    param.ManagerID = managerID;
    $.ajax({
        type: "POST",
        url: "Grid.aspx/GetEmployees",
        data: JSON.stringify(param),
        contentType: "application/json; charset=utf-8",
        dataType: 'json',
        success: function (data, textStatus) {
            if (textStatus == "success") {
                ReceivedClientData(JSON.parse(getMain(data)), subgrid_table_id);
            }
        },
        error: function (msg, error, state) {
            alert('An error has occured retrieving data!');
        }
    });
}

function ReceivedClientData(data, subgrid_table_id)
{
    var thegrid = jQuery("#" + subgrid_table_id);
    thegrid.clearGridData();
    for (var i = 0; i < data.length; i++)
        thegrid.jqGrid('addRowData', i + 1, data[i]);
}

function getMain(dObj)
{
    if (dObj.hasOwnProperty('d'))
        return dObj.d;  else   return dObj;
}

Now, we’ll get a new column(first column) in the parent jqGrid, using which we can get the sub grid which will contain employees who work under the corresponding Manager. The below image shows the output.

So, that’s all the functionality related to jqGrid that I wanted to discuss.

Some useful links that have good demos of jqGrid:

http://trirand.com/blog/jqgrid/jqgrid.html

http://www.deloy-dev.com/jgrid_demo_4.0/jqGrid_comprehensive_demo.html

Now, I will tell a couple of Errors that one might come across while following the above approach.

1. json is undefined

You might get a popup message in IE(version < 8) that says “‘json’ is undefined. The issue is that the JSON object is not available in IE 7. You’ll want to include JSON2.js on your page for IE < 8 users. This library checks if the browser supports JSON global object, if it supports it won’t do anything, otherwise this native implementation will be used. “json2.js” can be downloaded from this link.

2. Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.

You will come across this error when you are using JavaScriptSerializer. To fix this error, make sure you adjust maximum length of JSON string in your web.config. By default, the maximum length of a json string is 2097152 characters. If the serialized data that you send to client exceeds this default size then you should change this size accordingly. This size can changed programmatically through the JavaScriptSerializer.MaxJsonLength property or it can be changed in web.config as follows:

In code,

JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.MaxJsonLength = 2147483644;

In web.config,

<system.web.extensions>
    <scripting>
        <webServices>
            <jsonSerialization maxJsonLength="2147483644">
            </jsonSerialization>
        </webServices>
    </scripting>
</system.web.extensions>

Note: The “maxJsonLength” in web.config overrides the JavaScriptSerializer.MaxJsonLength property in code.

Phew!!……..that’s the end of a loooooooooong blog post…….😀

 
14 Comments

Posted by on May 27, 2012 in .NET, JavaScript, SharePoint

 

Tags: , , , , ,

Getting 12-hive/14-hive(Default SharePoint Installation path) path in SharePoint programmatically

If there’s one thing in SharePoint that every SharePoint Developer would know, it’s the path to 12-hive/14-hive.

In SharePoint 2010, it is
C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14

There will be cases when we need to use this path in our code. In such cases, instead of hard coding the path in our code(in which case, the code will break if SharePoint is installed on different Drive), we can use
an SPUtility method called GetGenericSetupPath() which returns the path of 12-hive/14-hive.


SPUtility.GetGenericSetupPath(String.Empty)

In SharePoint 2010, this method returns
C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14

If you want to get the path to a specific folder in 14-hive, all you have to do is pass in the relative path to that folder as parameter to the GetGenericSetupPath() method.
For example, if you want to get the path to the “FEATURES” folder in 14-hive, you would have to call the method as follows:


SPUtility.GetGenericSetupPath(@"TEMPLATE\FEATURES\")

This code will return the following output:

C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\FEATURES\

The same code works for 12-hive also.

SPUtility class has a lot of useful functions. We just have to look through it.

 
Leave a comment

Posted by on May 5, 2012 in SharePoint

 

Tags: , , , ,