Thursday, September 20, 2012

Override Content Query Webpart to do on the run filteration.

Problem

In one of my recent project I had a challenge of filtering the data with localization.
The project contained 2 languages, English and Spanish. I was trying to find out the out of the box way to get this done, but no help was available.


Solution

  1. Create a new empty project in Visual Studio 2010.
  2. Add a webpart to the project. "Webpart" not "Visual Web Part".
  3. Replace the Webpart class inherited and make it "Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart".
  4. Please have a look at the code below. It will have on the fly list settings and field settings applied to the webpart.
  5. Build your code and deploy it. It should work.



Source Code


#region System
using System;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
#endregion

// Create the Toolbar
namespace ToolBar.ToolBar
{
    [ToolboxItemAttribute(false)]
    public class ToolBar : Microsoft.SharePoint.Publishing.WebControls.ContentByQueryWebPart
    {
        #region Properties

        /// <summary>
        /// Root Site Name
        /// </summary>
        private string SiteUrl { get { return SPContext.Current.Site.RootWeb.Url; } }

        /// <summary>
        /// List Name
        /// </summary>
        private string ListName { get { return "ToolBar"; } }

        /// <summary>
        /// List of Url
        /// </summary>
        private string ListUrl
        {
            get
            {
                return string.Format("{0}/Lists/{1}/AllItems.aspx", SiteUrl, ListName);
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// On Page Load
        /// </summary>
        /// <param name="e"></param>
        protected override void OnInit(EventArgs e)
        {
            //On Load
            base.OnLoad(e);

            //General Settings
            this.AllowConnect = true;
            this.ShowUntargetedItems = false;
            this.AllowEdit = true;
            
            //this.FrameType = "NONE"; // TODO
            this.ChromeType = PartChromeType.None;
            this.ExportMode = WebPartExportMode.All;
            this.GroupByDirection = SortDirection.Desc;
            this.SortByDirection = SortDirection.Desc;
            
            this.ConnectionID = System.Guid.Empty;
            this.ListId = System.Guid.Empty;

            this.ViewFlag = "0";
            this.GroupingText = "GP Web Parts";
            this.Title = " Tool Bar";
            this.ContentTypeName = "Item";
            this.ItemStyle = "ToolBar";
            this.ServerTemplate = "100";
            this.GroupStyle = "DefaultHeader";
            this.WebUrl = "~sitecollection";
            this.Description = "Displays a dynamic view of content from your site.";
            this.Xsl = "<xsl:stylesheet xmlns:x=\"http://www.w3.org/2001/XMLSchema\" version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" xmlns:cmswrt=\"http://schemas.microsoft.com/WebPart/v3/Publishing/runtime\" exclude-result-prefixes=\"xsl cmswrt x\" > <xsl:import href=\"/Style Library/XSL Style Sheets/Header.xsl\" /> <xsl:import href=\"/Style Library/XSL Style Sheets/ItemStyle.xsl\" /> <xsl:import href=\"/Style Library/XSL Style Sheets/ContentQueryMain.xsl\" /> </xsl:stylesheet>";
            this.SampleData = "<dsQueryResponse><Rows><Row Title=\"Item 1\" LinkUrl=\"http://Item1\" Group=\"Group Header\" __begincolumn=\"True\" __begingroup=\"True\" /><Row Title=\"Item 2\" LinkUrl=\"http://Item2\" __begincolumn=\"False\" __begingroup=\"False\" /><Row Title=\"Item 3\" LinkUrl=\"http://Item3\" __begincolumn=\"False\" __begingroup=\"False\" /></Rows></dsQueryResponse>";
            this.ParameterBindings = string.Empty;

            //Root Web
            using (SPWeb _web = SPContext.Current.Site.RootWeb)
            {
                //Assign the list to the CQWP
                SPList _listYCLToolBar = _web.GetListFromUrl(ListUrl);

                //Data mapping
                this.DataMappings = string.Format("Description:|LinkUrl:{2},TargetUrl,URL;|Title:{4},Title,Text;|NumComments:|PublishedDate:|PostCategory:|ImageUrlAltText:{0},Title,Text;|Author:|Language:{3},Language,Lookup;|ImageUrl:{1},Icon,URL;|Body:|"
                    , _listYCLToolBar.Fields["Title"].Id.ToString()
                    , "{" + _listYCLToolBar.Fields["Icon"].Id.ToString() + "}"
                    , "{" + _listYCLToolBar.Fields["TargetUrl"].Id.ToString() + "}"
                    , "{" + _listYCLToolBar.Fields["Language"].Id.ToString() + "}"
                    , "{" + _listYCLToolBar.Fields["Title"].Id.ToString() + "}"
                    );

                this.ListGuid = _listYCLToolBar.ID.ToString();

                this.DataMappingViewFields = string.Format("{0},URL;{1},URL;{2},Text;{3},Lookup;"
                    , "{" + _listYCLToolBar.Fields["TargetUrl"].Id.ToString() + "}"
                    , "{" + _listYCLToolBar.Fields["Icon"].Id.ToString() + "}"
                    , "{" + _listYCLToolBar.Fields["Title"].Id.ToString() + "}"
                    , "{" + _listYCLToolBar.Fields["Language"].Id.ToString() + "}"
                    );
                
                //Filter One
                this.FilterField1 = "Language";//Custom Field to get the variation work.
                this.FilterOperator1 = FilterFieldQueryOperator.Eq;
                this.FilterValue1 = System.Globalization.CultureInfo.CurrentCulture.IetfLanguageTag;
                this.FilterType1 = "Lookup";
                this.Filter1ChainingOperator = FilterChainingOperator.Or;

                this.Filter2ChainingOperator = FilterChainingOperator.Or;

                //Sorting
                this.SortByFieldType = "DateTime";
                this.SortBy = "Created";
            }            
        }

        /// <summary>
        /// Create Child Controls
        /// </summary>
        protected override void CreateChildControls()
        {
            base.CreateChildControls();
        }

        #endregion
    }
}



Please let me know if there is any issue with this code or you can also correct me if I am wrong.

Sharepoint 2010 Error Solution : The Visible property cannot be set on Web Part 'g_XXX'. It can only be set on a standalone Web Part.

Problem

There is an issue with OOTB SharePoint logic.

Scenario

  1. 2 types of Users
    • User1 is Site Administrator
    • User2 is a simple user with Read, Limited Access.
  2. Issue comes under blog site.


How to reproduce? Follow the below given test case.

  1. Create a site collection. In my case I have created it on "http://pc92".
  2. I create a blog site with name "TestBlog" using User1. In my case it is "http://pc92/TestBlog/".
  3. Navigate to default blog created on site and add 11 comments. Right now the URL will be "http://pc92/TestBlog/Lists/Posts/Post.aspx?ID=1". This will enable the paging at the comments webpart.
  4. Now login with User2(Read, Limited Access User) and navigate to the URL "http://pc92/TestBlog/Lists/Posts/Post.aspx?ID=1". The page will show normal. The only difference here is you won't see the "Add Comment" box at the bottom of the screen.
  5. Click the next button. You will see the error.


Cause

The cause of the whole act is when you login as a User2(Read, Limited Access) the Comment Box(ListFormWebPart) is not available for this users. On page load everything is fine. But when you do a postback like paging event it bombs out. This might have got out of the site of MicroSoft Team.

Solution

I created 2 features, one is a site based feature(BlogFixSite) and another one web based feature(BlogFixWeb). On the BlogFixSite activation we check all the web which doesnot have BlogFixWeb activated and the template is a Blog template. So if the feature is activated I assume that the site is already having a fix. In the BlogFixWeb I go to the "Posts" folder and copy "Post.aspx" content and create "Post1.aspx". In the "Post1.aspx" i will remove the "ListFormWebPart" programatically. In "Post.aspx" at the bottom i will place a jquery which will check if the "Add Comment" box is missing than take the user to "Post1.aspx". Repeat the above given test cases and you should be all good.

BlogFixSite - C#

#region System
using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Administration;
using System.Collections.Generic;
using System.Linq;
#endregion

namespace BlogFix.Features.BlogFixSite
{
    [Guid("63c859cc-77ff-436e-a79e-b4619a67c7c3")]
    public class BlogFixSiteEventReceiver : SPFeatureReceiver
    {
        #region Events

        /// <summary>
        /// Activating
        /// </summary>
        /// <param name="properties"></param>
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            base.FeatureActivated(properties);

            try
            {
                //Site object
                SPSite _site = (SPSite)properties.Feature.Parent;

                if (_site != null)
                {
                    //Feature ID - BlogFixWeb
                    Guid _featureWeGuid = new Guid("c7178a85-ed45-4f27-bd4a-db2ec3eed95b");

                    //Web List
                    IList<SPWeb> _webList = (from _web in _site.AllWebs.Cast<SPWeb>()
                                             where string.Compare(_web.WebTemplate, "Blog", true) == 0
                                                 && _web.Features[_featureWeGuid] == null
                                             select _web).ToList();

                    //Change the post page
                    foreach (SPWeb _web in _webList)
                    {
                        //Enables the feature - BlogFixWeb
                        if (_web.Features[_featureWeGuid] == null)
                        {
                            _web.Features.Add(_featureWeGuid);
                        }
                    }
                }
            }
            catch (Exception Exc)
            {
                SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("BlogFixSiteEventReceiver - Activated", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, Exc.Message, Exc.StackTrace);
            }
        }

        #endregion
    }
}


BlogFixWeb - C#

#region System
using System;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Security;
using Microsoft.SharePoint.Administration;
using System.Collections.Generic;
using System.Linq;
#endregion

namespace BlogFix.Features.BlogFixWeb
{
    [Guid("564b0ad9-90a0-45fc-a672-901ec1fd111b")]
    public class BlogFixWebEventReceiver : SPFeatureReceiver
    {
        #region Events

        /// <summary>
        /// Activate the web based feautre
        /// </summary>
        /// <param name="properties"></param>
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            base.FeatureActivated(properties);

            try
            {
                SPWeb _web = (SPWeb)properties.Feature.Parent;

                //All the subfolders
                SPFolderCollection _folderCollection = _web.Folders["Lists"].SubFolders;

                //Finds the folder containing Post.aspx
                IList<SPFolder> _folderPostList = (from _folderX in _folderCollection.Cast<SPFolder>()
                                                   let _fName = _folderX.Name
                                                   let _fCount = (from _fileX in _folderX.Files.Cast<SPFile>()
                                                                  where string.Compare(_fileX.Name, "post.aspx", true) == 0
                                                                  select _fileX).Count()
                                                   where _fCount > 0
                                                   select _folderX).ToList();


                //Loop it if there are more than one
                if (_folderPostList != null)
                {
                    foreach (SPFolder _folderPost in _folderPostList)
                    {
                        //Get the Post file
                        SPFile _filePost = (from _fileX in _folderPost.Files.Cast<SPFile>()
                                            where string.Compare(_fileX.Name, "post.aspx", true) == 0
                                            select _fileX).FirstOrDefault();

                        //The file post to be copied
                        if (_filePost != null)
                        {
                            //New Post1.aspx URL
                            string _newPostUrl = string.Format("{0}/{1}/{2}",
                                _web.Url,
                                _folderPost.Url,
                                "Post1.aspx");

                            //Copy the file
                            _filePost.CopyTo(_newPostUrl, true);
                            
                            //Update the folder
                            _folderPost.Update();

                            //Remove the control once the page is copied
                            Microsoft.SharePoint.WebPartPages.SPLimitedWebPartManager _webPartCollection = _web.GetLimitedWebPartManager(_newPostUrl, System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);

                            //Retrive the webpart and remove
                            Microsoft.SharePoint.WebPartPages.WebPart _listFormWebPart = (from _wp in _webPartCollection.WebParts.Cast<Microsoft.SharePoint.WebPartPages.WebPart>()
                                                                                          where _wp.GetType().UnderlyingSystemType.Name == "ListFormWebPart"
                                                                                          select _wp).FirstOrDefault();


                            //Microsoft.SharePoint.WebPartPages.ListFormWebPart _XlistFormWebPart = (Microsoft.SharePoint.WebPartPages.ListFormWebPart)_listFormWebPart;
                            //string _webPartTitle = _XlistFormWebPart.Title
                            //return;

                            if (_listFormWebPart != null)
                            {
                                //Remove the webpart
                                _webPartCollection.DeleteWebPart(_listFormWebPart);

                                //Update
                                _web.Update();
                            }

                            //Modify Post1.aspx and add the javascript tag
                            if (_filePost.RequiresCheckout)
                            {
                                _filePost.CheckOut();
                            }

                            //Edit the file
                            byte[] _htmlByte = _filePost.OpenBinary();
                            string _html = System.Text.Encoding.ASCII.GetString(_htmlByte);
                            string _topHtml = _html.Substring(0, _html.LastIndexOf("</asp:Content>"));
                            string _stript = "\n<script language=\"javascript\" type=\"text/javascript\">!window.jQuery && document.write('<script src=\"http://code.jquery.com/jquery-1.4.2.min.js\"><\\/script>');</script>\n<script language='javascript'>\n\r$(document).ready(function(){\n\r\rif($(\"h3[class='ms-CommentHeader']\").length == 1){\n\r\r\r\rwindow.location.href =     window.location.href.replace(\".aspx?\" , \"1.aspx?\");\n\r\r}\n\r});</script>";
                                

                            _html = string.Format("{0}{1}</asp:Content>", _topHtml, _stript);
                            _filePost.SaveBinary(System.Text.Encoding.ASCII.GetBytes(_html));

                            //Check In
                            if (_filePost.RequiresCheckout)
                            {
                                _filePost.CheckIn(string.Empty);
                            }
                        }
                    }
                }

            }
            catch (Exception Exc)
            {
                SPDiagnosticsService.Local.WriteTrace(0, new SPDiagnosticsCategory("BlogFixWebEventReceiver - Activated", TraceSeverity.Unexpected, EventSeverity.Error), TraceSeverity.Unexpected, Exc.Message, Exc.StackTrace);
            }
        }

        #endregion
    }
}



JavaScript added on Post.aspx

There are 2 "<h3></h3>" tags on the post.aspx page. (1) is "Comments" and (2) is "Add Comments". So if a user dont have access he/she wont see "Add Comment" box. If that box doesnot exists on the page I simply send the user to Post1.aspx.
<script>    !window.jQuery && document.write('<script src="http://code.jquery.com/jquery-1.4.2.min.js"><\/script>');</script>
<script language='javascript'>
    $(document).ready(function () {
        if ($("h3[class='ms-CommentHeader']").length == 1) {
            window.location.href = window.location.href.replace(".aspx?", "1.aspx?");
        }
    });</script>


Final Comments

To make this feature available for all the webs just create a feature stapler which activates the web based feature just like its done in "BlogFixSite".
I know many people wont agree with my solution but right now it is what it is. Happy Coding. Do let me know if this helped you.

Tuesday, September 4, 2012

JQuery : Quick paging solution with JQuery

Goal

Many of the times we have to give the paging solutions with JQuery. I have created a small generic script that will allow you to apply paging on html table very quickly. Just a few settings and script will manage the paging(Note:- Right now the script will apply one one table on a page).

Settings

  1. Table ID : Apply an ID to the table with prefix "rpt". In our case we have used "rptPagingTable".
  2. Paging Items : All the rows which are going to be shown on the basis of page. Apply a custom tag to the row(tag="item").
  3. Next/Previous : The paging box will have a tag(tag="paging"). There are seperate tags assigned to the button Next and Previous. For Previous it is (tag="pre") and for Next it is (tag="next").


Solution

<html>
<head>
    <style type="text/css">
        body{font-family:Calibri,Verdana,Arial,Times New Roman;}
        .datagrid{border: 1px solid #808080;}
        .datagrid td{border: 1px solid #efefef;border-collapse: none;padding: 5px;}
        .header{background-color: #808080;color: #fff;text-align: center;font-size: 20px;}
        .itemtemplate{background-color: #fff;}
        .alternatingitemtemplate{background-color: #efefdf;}
        .footer{background-color: #808080;color: #fff;font-size: 15px;}
        .pager{background-color: #CED3D6;color: #000;font-size: 15px;text-align: center;}
        .pager a{color: #0877BD;}
        .pre{float: left;}
        .next{float: right;}
    </style>
    <script>!window.jQuery && document.write('<script src="http://code.jquery.com/jquery-1.4.2.min.js"><\/script>');</script>
    <script language="javascript" type="text/javascript">
        //CODE WHICH WILL HELP IN PAGING

        //Variable for paging
        var _currentPage = 1;
        var _pageSize = 0;
        var _totalPages = 1;

        $(document).ready(function () {

            //Setting the page size
            _pageSize = $("table[id*='rpt']").attr("pagesize");

            //Total Item
            var _trItem = $("tr[tag='item']");
            var _pager = $("tr[tag='paging']");

            //Calculate the page size
            _totalPages = parseInt($(_trItem).length / _pageSize) + ($(_trItem).length % _pageSize > 0 ? 1 : 0);

            //Hide the pager if the items are less than 13
            if ($(_trItem).length > _pageSize) {
                $(_pager).show();
            }
            else {
                $(_pager).hide();
            }

            //Page One Settings
            Paging();

            //Previous
            $("a[tag='pre']").click(function () {

                if (_currentPage == 1) {
                    return;
                }

                //Reduce the page index
                _currentPage = _currentPage - 1;

                //Paging
                Paging();
            });

            //Next
            $("a[tag='next']").click(function () {

                if (_currentPage == _totalPages) {
                    return;
                }

                //Increase the page index
                _currentPage = _currentPage + 1;

                //Paging
                Paging();
            });

        });

        //Shows/Hides the tr
        function Paging() {

            //Get the correct start index and end index
            var _endIndex = (_currentPage * _pageSize);
            var _startIndex = (_endIndex - _pageSize) + 1;

            //Hide all first
            $("tr[tag='item']").hide();

            //Show correct items
            $("tr[tag='item']").each(function (i) {
                var x = i + 1;
                if (x >= _startIndex && x <= _endIndex) {
                    $(this).show();
                }
            });
        }

    </script>
</head>
<body>
    <table id="rptPagingTable" cellpadding="0" cellspacing="0" border="0" class="datagrid"
        style="width: 700px" align="center" calculativegrid="PM" pagesize="5">
        <tr class="header"><td>No.</td><td style="width: 100%;">Instant Paging</td></tr>
        <tr tag="item" class='itemtemplate'><td>1</td><td>Ahmedabad</td></tr>
        <tr tag="item" class='alternatingitemtemplate'><td>2</td><td>Mumbai</td></tr>
        <tr tag="item" class='itemtemplate'><td>3</td><td>Delhi</td></tr>
        <tr tag="item" class='alternatingitemtemplate'><td>4</td><td>Calcutta</td></tr>
        <tr tag="item" class='itemtemplate'><td>5</td><td>Chicago</td></tr>
        <tr tag="item" class='alternatingitemtemplate'><td>6</td><td>New York</td></tr>
        <tr tag="item" class='itemtemplate'><td>7</td><td>New Jersy</td></tr>
        <tr tag="item" class='alternatingitemtemplate'><td>8</td><td>Bangalore</td></tr>
        <tr tag="item" class='itemtemplate'><td>9</td><td>Hyderabad</td></tr>
        <tr tag="item" class='alternatingitemtemplate'><td>10</td><td>Noida</td></tr>
        <tr tag="item" class='itemtemplate'><td>11</td><td>Mangalore</td></tr>
        <tr tag="item" class='alternatingitemtemplate'><td>12</td><td>Pune</td></tr>
        <tr tag="paging" class="pager">
            <td colspan="6">
                <a tag="pre" class="pre" href="javascript:void(0);"><< Prev</a>
                <a tag="next" class="next" href="javascript:void(0);">Next >></a>
            </td>
        </tr>
    </table>   
</body>
</html>


Display



Please let me know if this was helpful.

Tuesday, June 19, 2012

WCF Services : Simplest way to create a WCF REST(Representational State Transfer) service.

Goal

To create a WCF REST(Representational State Transfer) service. The main goal is to show it in the most simplest way.

Follow the below given steps

  1. Open Visual Studio 2010.
  2. File >> New Project >> WCF >> WCF Service Application. I named it SimpleWCFService.
  3. I removed the default "IService1.cs" and "Service1.svc". Added a new "Sample.svc". This will automatically create an interface in file "ISample.cs". The structure will look like the image given below.

ISample.cs

#region System
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Web;
#endregion

namespace SimpleWCFService
{
    [ServiceContract]
    public interface ISample
    {
        /// <summary>
        /// The parameters are passed in URL expression.
        /// </summary>
        /// <param name="Param1"></param>
        /// <param name="Param2"></param>
        /// <returns></returns>
        [OperationContract]
        [WebGet(UriTemplate = "SampleCheck/{Param1}/{Param2}")]
        string SampleCheckUrl(string Param1, string Param2);

        /// <summary>
        /// The parameters are passed as QueryString.
        /// </summary>
        /// <param name="Param1">{Param1}</param>
        /// <param name="Param2">{Param2}</param>
        /// <returns></returns>
        [OperationContract]
        [WebGet(UriTemplate = "SampleCheck?Param1={Param1}&Param2={Param2}")]
        string SampleCheckQueryString(string Param1, string Param2);
    }
}

Sample.svc.cs

#region System
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Web.Script.Serialization;
#endregion

namespace SimpleWCFService
{

    /// <summary>
    /// Simple Class
    /// </summary>
    [DataContract]
    public class SimpleClass
    {
        [DataMember]
        public string CParam1 { get; set; }

        [DataMember]
        public string CParam2 { get; set; }

        [DataMember]
        public string CMessage { get; set; }
    }


    /// <summary>
    /// The simplest way to use a WCF REST(Representational State Transfer) service
    /// </summary>
    public class Sample : ISample
    {

        /// <summary>
        /// The parameters are passed in URL expression.
        /// </summary>
        /// <param name="Param1"></param>
        /// <param name="Param2"></param>
        /// <returns></returns>
        public string SampleCheckUrl(string Param1, string Param2)
        {
            JavaScriptSerializer _jScript = new JavaScriptSerializer();
            SimpleClass _simple = new SimpleClass()
            {
                CParam1 = Param1,
                CParam2 = Param2,
                CMessage = "Success with SampleCheckUrl(int Param1, string Param2)!"
            };
            return _jScript.Serialize(_simple);            
        }

        /// <summary>
        /// The parameters are passed as QueryString.
        /// </summary>
        /// <param name="Param1"></param>
        /// <param name="Param2"></param>
        /// <returns></returns>
        public string SampleCheckQueryString(string Param1, string Param2)
        {
            JavaScriptSerializer _jScript = new JavaScriptSerializer();
            SimpleClass _simple = new SimpleClass()
            {
                CParam1 = Param1,
                CParam2 = Param2,
                CMessage = "Success with SampleCheckQueryString(int Param1, string Param2)!"
            };
            return _jScript.Serialize(_simple);
        }
    }
}

Web.config - MOST IMPORTANT

<?xml version="1.0"?>
<configuration> 
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SimpleWCFServiceBC">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
      <endpointBehaviors>
        <behavior name="restBehaviour">
          <webHttp automaticFormatSelectionEnabled="true" helpEnabled="true"/>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <bindings>
      <webHttpBinding>
        <binding name="RestBinding" />
      </webHttpBinding>
      <basicHttpBinding>

      </basicHttpBinding>
    </bindings>
    <services>
      <service name="SimpleWCFService.Sample" behaviorConfiguration="SimpleWCFServiceBC">
        <endpoint address="" binding="basicHttpBinding" contract="SimpleWCFService.ISample"></endpoint>
        <endpoint address="rest" binding="webHttpBinding" behaviorConfiguration="restBehaviour" bindingConfiguration="RestBinding" contract="SimpleWCFService.ISample"></endpoint>
        <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
      </service>
    </services>
  </system.serviceModel>

  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>

</configuration>

Checking REST Service - URL FORMAT

Navigate to the below given url(Note:Replace the port number.).
"http://{LOCALHOST:PORTNUMBER}/Sample.svc/rest/SampleCheck/Args1/Args2".
This should give the result in below given format.
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">{"CParam1":"Args1","CParam2":"Args2","CMessage":"Success with SampleCheckUrl(int Param1, string Param2)!"}</string> 

Checking REST Service - QueryString

Navigate to the below given url(Note:Replace the port number.).
"http://{LOCALHOST:PORTNUMBER/Sample.svc/rest/SampleCheck?Param1=Args1&Param2=Args2".
This should give the result in below given format.
<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">{"CParam1":"Args1","CParam2":"Args2","CMessage":"Success with SampleCheckQueryString(int Param1, string Param2)!"}</string> 


I spent a lot of time trying to figure this out so writing this up for the ones who just want solutions. Let me know if this helped.

Friday, June 8, 2012

SQL Server : Using CTE(Common Table Expression) fetch a Tree View

Goal

Using CTE show a complete tree view.

Challenge

When a developer tries to search CTE in internet, there are millions of articles which already exists which says to get a tree view using CTE. But there is a problem with most of them. They dont give correct output. For making the data show in correct way users should enter data in an order, else it wont come. So first I will explain how the examples on internet mislead.


SQL - Create temp table

-- DECLARE
DECLARE @Company AS TABLE(EmpID INT, ParentID INT, PersonName VARCHAR(100));

-- Insert Temp Records
INSERT INTO @Company(EmpID, ParentID, PersonName) 
VALUES(1 , NULL , 'Maulik Dhorajia')
    , (2 , NULL , 'Bhavesh Gohel')
    , (3 , NULL , 'Dinesh Padhiyar')
    , (4 , 2 , 'Vijay Kumar')
    , (5 , 1 , 'Jitendra Makwana')
    , (6 , 4 , 'Jayesh Dhobi')
    , (7 , 1 , 'Shivpalsinh Jhala')
    , (8 , 5 , 'Amit Patel')
    , (9 , 3 , 'Abidali Suthar')

-- Default data
SELECT * FROM @Company;



SQL - Which usually misleads to get correct tree view

-- Incorrect result which we usually find on Internet
;WITH CTECompany
AS
(
    SELECT EmpID, ParentID, PersonName , 0 AS HLevel 
    FROM @Company
    WHERE ParentID IS NULL
    
    UNION ALL
    
    SELECT C.EmpID, C.ParentID, C.PersonName , (CTE.HLevel + 1) AS HLevel 
    FROM @Company C
    INNER JOIN CTECompany CTE ON CTE.EmpID = C.ParentID
    WHERE C.ParentID IS NOT NULL
)

-- Misleading SQL
SELECT * FROM
(
    SELECT 
        EmpID
        , ParentID
        , HLevel
        , (REPLICATE( '----' , HLevel ) + PersonName) AS Person
    FROM CTECompany
) AS P
ORDER BY HLevel;




SQL - Correct CTE with Tree View

-- Working Example
;WITH CTECompany
AS
(
    SELECT 
        EmpID, 
        ParentID, 
        PersonName , 
        0 AS HLevel,
        CAST(RIGHT(REPLICATE('_',5) +  CONVERT(VARCHAR(20),EmpID),20) AS VARCHAR(MAX)) AS OrderByField
    FROM @Company
    WHERE ParentID IS NULL
    
    UNION ALL
    
    SELECT 
        C.EmpID, 
        C.ParentID, 
        C.PersonName , 
        (CTE.HLevel + 1) AS HLevel,
        CTE.OrderByField + CAST(RIGHT(REPLICATE('_',5) +  CONVERT(VARCHAR(20),C.EmpID),20) AS VARCHAR(MAX)) AS OrderByField
    FROM @Company C
    INNER JOIN CTECompany CTE ON CTE.EmpID = C.ParentID
    WHERE C.ParentID IS NOT NULL
)

-- Working Example
SELECT 
    EmpID
    , ParentID
    , HLevel
    , PersonName
    , (REPLICATE( '----' , HLevel ) + PersonName) AS Person
FROM CTECompany
ORDER BY OrderByField,PersonName;


Hope this helped and saved a lot of your time!

Reply for the comment "Nice try. still relies on order of data entry. add (44 , 1 , 'XXitendra Makwana')". I tried to see what was wrong but I can see the correct tree with the same query. Not sure what is wrong. Can anyone else see the issue and post a comment? Thanks in advance.

Saturday, May 19, 2012

PowerShell : Mount-SPContentDatabase / Dismount-SPContentDatabase database

Situation

If a client provides a database backup of the SharePoint site running on their server and we want to use that database in our environment. Using powershell we have to change the mounting of database and use the database what client sent.

Prerequesites

Restore the database given by client in your Sharepoint database instance. Once that is done run the below given powershell in "SharePoint 2010 Management Shell".

Powershell

# Url of the site collection which the database is going to point
$SiteUrl = "http://MyMachine"
# The current database site is using.
$CurrentContentDatabase = "WSS_Content"
# New database which the site will point
$NewContentDatabase = "WSS_Content_NewDB"
# SQL SERVER Instanne
$DatabaseServer = "MYMachine\SHAREPOINT"

# The command will dismount the current databae
Dismount-SPContentDatabase -Identity $CurrentContentDatabase -confirm:$false

# Mounts the new database
Mount-SPContentDatabase -name $NewContentDatabase -DatabaseServer $DatabaseServer -WebApplication $SiteUrl -confirm:$false

Friday, May 18, 2012

Apply "Multiple" column Group By on DataTable in C#

Goal

A few days ago a reader asked for Multiple column group by on DataTable. This blos is in reply of that question. This was my old blog Apply group by clause on Datatable in C#.

Solution

#region System
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
#endregion

namespace SampleApplication
{
    #region Enums

    /// <summary>
    /// The functions which can be used for aggreation
    /// </summary>
    public enum AggregateFunction
    {
        Sum,
        Avg,
        Count,
        Max,
        Min
    }

    #endregion

    #region Entity

    /// <summary>
    /// The class which will have properties of function to be performed and on which field
    /// </summary>
    public class DataTableAggregateFunction
    {
        /// <summary>
        /// The function to be performed
        /// </summary>
        public AggregateFunction enmFunction { get; set; }

        /// <summary>
        /// Performed for which column
        /// </summary>
        public string ColumnName { get; set; }

        /// <summary>
        /// What should be the name after output
        /// </summary>
        public string OutPutColumnName { get; set; }
    }

    #endregion
    

    public class Helper
    {
        /// <summary>
        /// Demo for Group By
        /// </summary>
        public void DemoGroupBy()
        { 
            //Gets the mock data table
            DataTable _dt = GetDataTable();

            //Add columns which you want to group by
            IList<string> _groupByColumnNames = new List<string>();
            _groupByColumnNames.Add("State");
            _groupByColumnNames.Add("City");

            //Functions you want to perform on which fields
            IList<DataTableAggregateFunction> _fieldsForCalculation = new List<DataTableAggregateFunction>();
            _fieldsForCalculation.Add(new DataTableAggregateFunction() { enmFunction = AggregateFunction.Avg, ColumnName = "Population", OutPutColumnName = "PopulationAvg" });
            _fieldsForCalculation.Add(new DataTableAggregateFunction() { enmFunction = AggregateFunction.Sum, ColumnName = "Population", OutPutColumnName = "PopulationSum" });
            _fieldsForCalculation.Add(new DataTableAggregateFunction() { enmFunction = AggregateFunction.Count, ColumnName = "Population", OutPutColumnName = "PopulationCount" });
            _fieldsForCalculation.Add(new DataTableAggregateFunction() { enmFunction = AggregateFunction.Max, ColumnName = "Year", OutPutColumnName = "YearMax" });
            _fieldsForCalculation.Add(new DataTableAggregateFunction() { enmFunction = AggregateFunction.Min, ColumnName = "Year", OutPutColumnName = "YearMin" });

            //Gets the result after grouping by
            DataTable dtGroupedBy = GetGroupedBy(_dt, _groupByColumnNames, _fieldsForCalculation);
        }


        /// <summary>
        /// Returns a mock data table
        /// </summary>
        /// <returns></returns>
        private DataTable GetDataTable()
        {
            //Declarations
            DataTable _dt = new DataTable();
            DataRow _dr;
            
            //Create columns
            _dt.Columns.Add(new DataColumn() { ColumnName = "State" });
            _dt.Columns.Add(new DataColumn() { ColumnName = "City" });
            _dt.Columns.Add(new DataColumn() { ColumnName = "Year", DataType = typeof(System.Int32) });
            _dt.Columns.Add(new DataColumn() { ColumnName = "Population", DataType = typeof(System.Int32) });

            //Add mock data
            _dr = _dt.NewRow(); _dr["State"] = "Gujarat"; _dr["City"] = "Ahmedabad"; _dr["Year"] = 2009; _dr["Population"] = 6000000; _dt.Rows.Add(_dr);
            _dr = _dt.NewRow(); _dr["State"] = "Gujarat"; _dr["City"] = "Surat"; _dr["Year"] = 2009; _dr["Population"] = 2000000; _dt.Rows.Add(_dr);
            _dr = _dt.NewRow(); _dr["State"] = "Gujarat"; _dr["City"] = "Rajkot"; _dr["Year"] = 2009; _dr["Population"] = 1000000; _dt.Rows.Add(_dr);
            _dr = _dt.NewRow(); _dr["State"] = "Maharastra"; _dr["City"] = "Mumbai"; _dr["Year"] = 2009; _dr["Population"] = 3000000; _dt.Rows.Add(_dr);
            _dr = _dt.NewRow(); _dr["State"] = "Maharastra"; _dr["City"] = "Pune"; _dr["Year"] = 2009; _dr["Population"] = 3000000; _dt.Rows.Add(_dr);

            _dr = _dt.NewRow(); _dr["State"] = "Gujarat"; _dr["City"] = "Ahmedabad"; _dr["Year"] = 2010; _dr["Population"] = 8000000; _dt.Rows.Add(_dr);
            _dr = _dt.NewRow(); _dr["State"] = "Maharastra"; _dr["City"] = "Mumbai"; _dr["Year"] = 2010; _dr["Population"] = 8000000; _dt.Rows.Add(_dr);

            _dr = _dt.NewRow(); _dr["State"] = "Gujarat"; _dr["City"] = "Ahmedabad"; _dr["Year"] = 2011; _dr["Population"] = 6000000; _dt.Rows.Add(_dr);
            _dr = _dt.NewRow(); _dr["State"] = "Gujarat"; _dr["City"] = "Surat"; _dr["Year"] = 2011; _dr["Population"] = 2000000; _dt.Rows.Add(_dr);
            _dr = _dt.NewRow(); _dr["State"] = "Gujarat"; _dr["City"] = "Rajkot"; _dr["Year"] = 2011; _dr["Population"] = 1000000; _dt.Rows.Add(_dr);
            _dr = _dt.NewRow(); _dr["State"] = "Maharastra"; _dr["City"] = "Mumbai"; _dr["Year"] = 2011; _dr["Population"] = 3000000; _dt.Rows.Add(_dr);
            _dr = _dt.NewRow(); _dr["State"] = "Maharastra"; _dr["City"] = "Pune"; _dr["Year"] = 2011; _dr["Population"] = 3000000; _dt.Rows.Add(_dr);

            //Return table
            return _dt;
        }

        /// <summary>
        /// Group by DataTable
        /// </summary>
        /// <param name="_dtSource"></param>
        /// <param name="_groupByColumnNames"></param>
        /// <param name="_fieldsForCalculation"></param>
        /// <returns></returns>
        private DataTable GetGroupedBy(DataTable _dtSource, IList<string> _groupByColumnNames, IList<DataTableAggregateFunction> _fieldsForCalculation)
        {
            
            //Once the columns are added find the distinct rows and group it bu the numbet
            DataTable _dtReturn = _dtSource.DefaultView.ToTable(true, _groupByColumnNames.ToArray());

            //The column names in data table
            foreach (DataTableAggregateFunction _calculatedField in _fieldsForCalculation)
            {
                _dtReturn.Columns.Add(_calculatedField.OutPutColumnName);
            }

            //Gets the collection and send it back
            for (int i = 0; i < _dtReturn.Rows.Count; i = i + 1)
            {
                #region Gets the filter string
                string _filterString = string.Empty;
                for (int j = 0; j < _groupByColumnNames.Count; j = j + 1)
                {
                    if (j > 0)
                    {
                        _filterString += " AND ";
                    }
                    if (_dtReturn.Columns[_groupByColumnNames[j]].DataType == typeof(System.Int32))
                    {
                        _filterString += _groupByColumnNames[j] + " = " + _dtReturn.Rows[i][_groupByColumnNames[j]].ToString() + "";
                    }
                    else
                    {
                        _filterString += _groupByColumnNames[j] + " = '" + _dtReturn.Rows[i][_groupByColumnNames[j]].ToString() + "'";
                    }
                }
                #endregion

                #region Compute the aggregate command

                foreach (DataTableAggregateFunction _calculatedField in _fieldsForCalculation)
                {
                    _dtReturn.Rows[i][_calculatedField.OutPutColumnName] = _dtSource.Compute(_calculatedField.enmFunction.ToString() + "(" + _calculatedField.ColumnName + ")", _filterString);
                }
                
                #endregion
            }

            return _dtReturn;
        }
    }
}


How to Use?

Call the function from somewhere.
(new SampleApplication.Helper()).DemoGroupBy();


Screenshot

Before Group By :
After Group By :


Please let me know if this helped.

Thursday, May 17, 2012

SharePoint 2010 : Placing the SharePoint:PeopleEditor icons on right instead of bottom.

Goal

The SharePoint:PeopleEditor show the icons on the right side instead of bottom.

Sample Page : ASPX

<style type="text/css">
        .ppimg{border: 0px;vertical-align: middle !important;}
    </style>
    <br /><br /><br /><br />
    <table border="0" cellpadding="10" cellspacing="5" style="border:1px solid #808080">
        <tr>
            <td colspan="2">People picker on right instead of bottom</td>
        </tr>
        <tr>
            <td>
                Default
            </td>
            <td>
                <SharePoint:PeopleEditor AllowEmpty="false" ValidatorEnabled="true" ID="ppDefault"
                    runat="server" ShowCreateButtonInActiveDirectoryAccountCreationMode="false" SelectionSet="User,SPGroup"
                    Rows="1" Width="300px" />
            </td>
        </tr>
        <tr>
            <td nowrap="nowrap">
                Custom View
            </td>
            <td>
                <table>
                    <tr>
                        <td>
                            <SharePoint:PeopleEditor AllowEmpty="false" ValidatorEnabled="true" ID="ppCustom"
                                runat="server" ShowCreateButtonInActiveDirectoryAccountCreationMode="false" SelectionSet="User,SPGroup"
                                Rows="1" Width="300px" ShowButtons="false" />
                        </td>
                        <td valign="top">
                            <a id="aPPChekNames" runat="server" title="Check Name" href="javascript:">
                                <img src="/_layouts/images/checknames.png" alt="Check Names" class="ppimg" /></a>
                        </td>
                        <td valign="top">
                            <a id="aPPBrowse" runat="server" title="Browse" href="javascript:">
                                <img src="/_layouts/images/addressbook.gif" alt="Browse" class="ppimg" /></a>
                        </td>
                    </tr>
                </table>
            </td>
        </tr>
    </table>


Sample Page : Code

//Apply the Javascript to the Icons
aPPChekNames.Attributes.Add("onclick", "if(!ValidatePickerControl('" + ppCustom.ClientID + "')){ ShowValidationError(); return false;} var arg=getUplevel('" + ppCustom.ClientID + "'); var ctx='" + ppCustom.ClientID + "';EntityEditorSetWaitCursor(ctx);WebForm_DoCallback('" + ppCustom.UniqueID + "',arg,EntityEditorHandleCheckNameResult,ctx,EntityEditorHandleCheckNameError,true);return false;");
aPPBrowse.Attributes.Add("onclick", "javascript:__Dialog__" + ppCustom.ClientID + "(); return false;");


Result



I am not sure if many know about this or this is normal, but just wanted to share. Let me know if this helped.

SharePoint 2010 : Call server side function from XSL.

Goal

To use a server side function from a XSL used in a web part.

Solution

It is simple to use a server side function. Just need to make sure the dll should be deployed in GAC. In this example I created a class with name "ClassName", the project name is "SampleApplication". Deployed the dll in GAC. Modify the XSL head and add xmlns:helper="SampleApplication.ClassName" on the head. "helper" is like an tag which will point to the class.

XSL
<xsl:stylesheet 
  version="1.0" 
  exclude-result-prefixes="x d xsl msxsl cmswrt helper"
  xmlns:x="http://www.w3.org/2001/XMLSchema" 
  xmlns:d="http://schemas.microsoft.com/sharepoint/dsp" 
  xmlns:ddwrt="http://schemas.microsoft.com/WebParts/v2/DataView/runtime"
  xmlns:cmswrt="http://schemas.microsoft.com/WebParts/v3/Publishing/runtime"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"
  xmlns:helper="SampleApplication.ClassName">


SampleApplication.ClassName
#region System
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#endregion

namespace SampleApplication
{
    public class ClassName
    {
        #region Properties
        
        /// <summary>
        /// Name 
        /// </summary>
        private static string Name = "Maulik Dhorajia";
        
        /// <summary>
        /// Today Date
        /// </summary>
        private static string TodayDate = System.DateTime.Now.ToString();

        #endregion

        /// <summary>
        /// Gets the property
        /// </summary>
        /// <param name="propertyKey"></param>
        /// <returns></returns>
        public static string GetProperty(string propertyKey)
        {
            if (propertyKey == "Name")
            {
                return Name;
            }
            else if (propertyKey == "TodayDate")
            {
                return TodayDate;
            }
            else 
            {
                return "";
            }
        }
    }
}


Calling the function from XSL
<xsl:value-of select="helper:GetLabel('Name')"/>
<xsl:value-of select="helper:GetLabel('TodayDate')"/>


This should be all to show the stuff on the page. Let me know if this helped.

Wednesday, May 16, 2012

SharePoint 2010 : Insert custom icon in a custom group on Ribbon of Document Library.

Goal

Insert a custom icon(Email icon) on the SharePoint Document Library. The Icon should show in a Custom Group.

Challange

I thought getting the Icon on the top was easy(actually it was easy to place an icon on the top). But to place the icon in a Custom Group was a bit tough that it seems.

Follow the steps to achieve

  1. Open Visual Studio 2010, Create an empty SharePoint Application.
  2. Add a Module, Edit the Element.xml
    <?xml version="1.0" encoding="utf-8"?>
    <Elements xmlns="http://schemas.microsoft.com/sharepoint/">
      <CustomAction
          Id="EmailDocsCustom"
          Location="CommandUI.Ribbon"
          RegistrationType="ContentType"
          RegistrationId="0x01">
        <CommandUIExtension>
          <CommandUIDefinitions>
            <CommandUIDefinition Location="Ribbon.Documents.Groups._children">
              <Group
                  Id="EmailDocsCustomGroup"
                  Sequence="100"
                  Description="E-Mail Controls"
                  Title="E-Mail Controls"
                  Template="EmailDocsCustomGroupTemplate">
                <Controls Id="EmailDocsCustomGroupControl">
                  <Button
                      Id="EmailDocsCustomGroupControlButton"
                      Sequence="5"
                      Command="SimpleAlert"
                      Image32by32="/_layouts/1033/images/formatmap32x32.png" Image32by32Left="-448" Image32by32Top="-128"
                      ToolTipTitle="Email Documents"
                      ToolTipDescription="Select the documents which you want to email!"
                      LabelText="Email Selected Documents"
                      TemplateAlias="o1" />
                </Controls>
              </Group>
            </CommandUIDefinition>
            <CommandUIDefinition Location="Ribbon.Templates._children">
              <GroupTemplate Id="EmailDocsCustomGroupTemplate">
                <Layout Title="LargeLarge">
                  <OverflowSection Type="OneRow" TemplateAlias="o1" DisplayMode="Large"/>
                  <OverflowSection Type="OneRow" TemplateAlias="o2" DisplayMode="Large"/>
                </Layout>
              </GroupTemplate>
            </CommandUIDefinition>
            <CommandUIDefinition Location="Ribbon.Documents.Scaling._children">
              <MaxSize Id="Ribbon.Documents.Scaling.Custom.MaxSize" Sequence="15" GroupId="EmailDocsCustomGroup" Size="LargeLarge" />
            </CommandUIDefinition>
          </CommandUIDefinitions>
          <CommandUIHandlers>
            <CommandUIHandler Command="SimpleAlert" CommandAction="javascript:OpenEmailPage();" />
          </CommandUIHandlers>
        </CommandUIExtension>
      </CustomAction>
    
      <CustomAction
       Id="EmailRelatedScript"
       Location="ScriptLink"
       ScriptSrc ="/_layouts/WhatEvePathYouHave/EmailScript.js"/>
      
    </Elements>
    
  3. Place the EmailScript.js at C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\LAYOUTS\WhatEvePathYouHave(You can map the folder and do this). With the help of this script you can do manipulations on the click of the icon placed on the top ribbon. Below is the script in EmailScript.js.
    //Get the current user and add the values found in the Email settings list
    function OpenEmailPage() {
    
        //Gets the current Context
        var _ctx = new SP.ClientContext.get_current();
        //Selected Items Variables
        var _itemIds = "";
        //Get current list id
        var _listId = SP.ListOperation.Selection.getSelectedList();
        var _listUrl = window.location.href;
        //get all selected list items
        var _selectedItems = SP.ListOperation.Selection.getSelectedItems(_ctx);
        //collect selected item ids
        for (var i = 0; i < _selectedItems.length; i++) {
            if (_itemIds == "")
            { _itemIds += _selectedItems[i].id; }
            else
            { _itemIds += "," + _selectedItems[i].id; }
        }
    
        alert('ListID = ' + _listId + "\nListUrl = " + _listUrl + "\nIDs=" + _itemIds);    
    }
  4. Before deploying the solution, check a document library. The ribbon on the top in the documents should like the below given image.
  5. Deploy the solution. Visit a Document Library, select docs and the new Icon should show up on the top right corner.
I am not very good with Ribbons and spent a lot of time to get just this portion. So thought if this can help many other who dont have time and have to get this quickly.