Monthly Archives: July 2014

Create Custom Http Handler in SharePoint

I would like to share my experience in Creating Custom Http Handler in SharePoint. There are couple of ways to create Http Handler project, but I prefer to use Ashx Handler template from CKSDev Development Tools for SharePoint. It is an easier way to start your Http Handler project and to deploy the solution.

 

CKSDev installation issue

You have to download and install the tool based on your Visual Studio version, so make sure you’re downloading the right version. The other thing that I would like to mention is when I add new item “Ashx handler”, there is a pop up error message look like:

Could not load file or assembly ‘file:///C:\USERS\XXX\APPDATA\LOCAL\MICROSOFT\VISUALSTUDIO\12.0\EXTENSIONS\WCFZLEFI.TYX\CKS.Dev12.Cmd.Imp.v4.dll’
or one of its dependencies.
This assembly is built by a runtime newer than the currently loaded runtime and cannot be loaded.

Solution to this is to copy all CKS dll into Global Assembly Cache using gacutil command in Visual Studio command prompt. If you don’t face the issue, then probably they have already fixed it.

 

Pre-Requisite

  • CKSDev Tools for SharePoint installed
  • List “MyList” exists in Root Web

 

Implementation 

So without further ado, lets start create one.

1. Create SharePoint Empty Project, called it CustomHttpHandler and Click OK.
CustomHttpHandler1
2. Enter your Site URL and Deploy it as farm solution and Click Finish.
3. Right Click CustomHttpHandler project > Add > New Item, then Select SharePoint 2013 – Ashx handler (CKSDev) template, called it MyAshxHandler and Click Add.
CustomHttpHandler2
4. Now, the solution explorer will look like below and Set the Build Action of MyAshxHandler.ashx to Content.
CustomHttpHandler4
5. Include MyAshxHandler module in the CustomHttpHandler Feature.
CustomHttpHandler4
6. Update MyAshxHandler.ashx.cs code to below:
using System;
using System.Web;
using System.Runtime.InteropServices;
using Microsoft.SharePoint;
using System.Linq;

namespace CustomHttpHandler
{
    [Guid("6aa41c89-943a-4242-824b-85c2f433ede3")]
    public partial class MyAshxHandler : IHttpHandler
    {
        private const string ParameterName = "Title";
        private const string ColumnName = "Title";
        #region IHttpHandler Members

        /// <summary>
        /// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false.
        /// </returns>
        public bool IsReusable
        {
            get { return false; }
        }

        /// <summary>
        /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param>
        public void ProcessRequest(HttpContext context)
        {
            try
            {
                context.Response.Clear();

                // Get Title parameter
                string title = GetParameters(ParameterName);
                if(title != string.Empty)
                {
                    Guid siteId = SPContext.Current.Site.ID;

                    // Elevate permission for Anonymous
                    SPSecurity.RunWithElevatedPrivileges(delegate
                    {
                        using (SPWeb web = new SPSite(siteId).OpenWeb())
                        {
                            // Get List
                            SPList myList = web.Lists.Cast<SPList>().Where(lst => lst.Title == "MyList").FirstOrDefault();
                            if (myList == null)
                                throw new Exception("Bad Request, My list is NOT found ...");

                            // Get List item
                            SPListItem myItem = myList.Items.Cast<SPListItem>().Where(strslt => strslt[ColumnName].ToString() == title).FirstOrDefault();

                            // Add / Update Item
                            web.AllowUnsafeUpdates = true;

                            // If new item
                            if (myItem == null)
                                myItem = myList.Items.Add();

                            myItem[ColumnName] = title;
                            myItem.Update();

                            web.AllowUnsafeUpdates = false;

                            // Set the response
                            context.Response.StatusCode = 200;
                            context.Response.StatusDescription = "Successful";
                            context.Response.ContentType = "text/plain";
                            context.Response.Write(String.Format("Item with GUID:{0} and Title:{1} has been created / updated", myItem.ID.ToString(), myItem[ColumnName].ToString()));
                        }
                    });
                }
            }
            catch(Exception ex)
            {
                context.Response.Clear();
                context.Response.StatusCode = 400;
                context.Response.StatusDescription = ex.Message;
            }
            context.Response.End();
        }
        #endregion

        #region private methods
        private string GetParameters(string queryParam)
        {
            try
            {
                return HttpContext.Current.Request.QueryString[queryParam];
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }
        #endregion
    }
}
7. Build and Deploy the solution.

 

Results

CustomHttpHandler5

CustomHttpHandler6

 

References

Advertisements

Enable SharePoint 2013 Search REST Queries for Anonymous

Recently in my project, I have to enable / call SharePoint REST Search Queries for Anonymous users. The reason behind that is because the site is public external site and I would like the search box to return search results as suggestions for Anonymous users. There is msdn article to do that step by step, but in this article I would like to share my own experience and knowledge on how did I enable SharePoint 2013 Search REST Queries for Anonymous users.

Pre-Requisites

1. Web Application and Site Collection created with Anonymous Access configured
2. Search Service Application working, Full Crawled completed successfully and Search Setting configured
3. Search Input Box ID must contains “_csr_sbox”

Solution

Without further ado, please find below is the list of steps:

1. Run EnableAnonymousRESTSearch.ps1 and Enter Root Web URL. This PowerShell script will create QueryPropertiesTemplate library in root web and upload QueryParameterTemplate.xml into the library to override QueryProperties element for Anonymous users. It also deactivate ViewFormPagesLockDown Site Collection Feature when activated.

Param(
[string] $url = $(Read-Host -prompt "Enter Site Collection URL: ")
)

# Include SharePoint reference
if ( (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null )
{
    Add-PsSnapin Microsoft.SharePoint.PowerShell
}

# Get All variables required
$farmID=(Get-SPFarm).Id
$spSite=Get-SPSite $url
$siteColId=$spSite.Id
$topLevelSiteID=$spSite.RootWeb.Id
$rootWeb=$spSite.RootWeb
$featureLockDownFolderName = "ViewFormPagesLockDown"

# Deactivate ViewFormPagesLockDown Site Feature
$featureLockDown = Get-SPFeature -Site $url | Where {$_.DisplayName -eq $featureLockDownFolderName}
if($featureLockDown -ne $null)
{
   Disable-SPFeature $featureLockDown -url $url
   Write-Host "ViewFormPagesLockDown Deactivated ... "
}

# Create QueryPropertiesTemplate Library and Upload QueryParameterTemplate.xml into the library
if($null -ne $rootWeb)
{
	$list=$rootWeb.Lists["QueryPropertiesTemplate"]
		if($null -eq $list)
		{
			$template=$rootWeb.ListTemplates["Document Library"]
			$list=$rootWeb.Lists[$rootWeb.Lists.Add("QueryPropertiesTemplate","QueryPropertiesTemplate",$template)]
		}

	$scriptFile = $MyInvocation.MyCommand.Definition
	[string]$currentSource = Get-Content $scriptFile
	[int]$startScript=$currentSource.LastIndexOf("<?xml ");
	[int]$closeComment=$currentSource.LastIndexOf("#>");
	$xmlFile=[xml]($currentSource.Substring($startScript,$closeComment-$startScript))
	$xmlFile.QueryPropertiesTemplate.QueryProperties.FarmId=$farmID.ToString()
	$xmlFile.QueryPropertiesTemplate.QueryProperties.SiteId=$siteColId.ToString()
	$xmlFile.QueryPropertiesTemplate.QueryProperties.WebId=$topLevelSiteID.ToString()

	$xmlFile.OuterXml | Out-File queryparametertemplate.xml -Encoding UTF8
	$tempFile=Get-Item -LiteralPath "queryparametertemplate.xml"

	$folder = $list.RootFolder
	$stream=$tempFile.OpenRead()
	$file = $folder.Files.Add($folder.Url+"/queryparametertemplate.xml",$stream,$true, "created by script",$false)
	$stream.Close()

	if($null -ne $file)
	{
		Write-Host ("File Created At " + $rootWeb.Url + "/" + $file.Url)
	}
	Write-Host " "
}

<#
<?xml version="1.0" encoding="utf-8"?>
<QueryPropertiesTemplate xmlns="http://www.microsoft.com/sharepoint/search/KnownTypes/2008/08" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <QueryProperties i:type="KeywordQueryProperties">
    <EnableStemming>true</EnableStemming>
    <FarmId>22222222-fa49-4987-b1ea-9fad99e81a0f</FarmId>
    <SiteId>11111111-68f0-41c5-a525-7e7fda7666b3</SiteId>
    <WebId>00000000-8d97-40a2-b07b-cf05e4c85084</WebId>
    <IgnoreAllNoiseQuery>true</IgnoreAllNoiseQuery>
    <KeywordInclusion>AllKeywords</KeywordInclusion>
    <SummaryLength>180</SummaryLength>
    <TrimDuplicates>true</TrimDuplicates>
    <WcfTimeout>120000</WcfTimeout>
    <Properties xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
      <a:KeyValueOfstringanyType>
        <a:Key>_IsEntSearchLicensed</a:Key>
        <a:Value i:type="b:boolean" xmlns:b="http://www.w3.org/2001/XMLSchema">true</a:Value>
      </a:KeyValueOfstringanyType>
      <a:KeyValueOfstringanyType>
        <a:Key>EnableSorting</a:Key>
        <a:Value i:type="b:boolean" xmlns:b="http://www.w3.org/2001/XMLSchema">true</a:Value>
      </a:KeyValueOfstringanyType>
      <a:KeyValueOfstringanyType>
        <a:Key>MaxKeywordQueryTextLength</a:Key>
        <a:Value i:type="b:int" xmlns:b="http://www.w3.org/2001/XMLSchema">4096</a:Value>
      </a:KeyValueOfstringanyType>
      <a:KeyValueOfstringanyType>
        <a:Key>TryCache</a:Key>
        <a:Value i:type="b:boolean" xmlns:b="http://www.w3.org/2001/XMLSchema">true</a:Value>
      </a:KeyValueOfstringanyType>
    </Properties>
    <PropertiesContractVersion>15.0.0.0</PropertiesContractVersion>
    <EnableFQL>false</EnableFQL>
    <EnableSpellcheck>Suggest</EnableSpellcheck>
    <EnableUrlSmashing>true</EnableUrlSmashing>
    <IsCachable>false</IsCachable>
    <MaxShallowRefinementHits>100</MaxShallowRefinementHits>
    <MaxSummaryLength>185</MaxSummaryLength>
    <MaxUrlLength>2048</MaxUrlLength>
    <SimilarType>None</SimilarType>
    <SortSimilar>true</SortSimilar>
    <TrimDuplicatesIncludeId>0</TrimDuplicatesIncludeId>
    <TrimDuplicatesKeepCount>1</TrimDuplicatesKeepCount>
  </QueryProperties>
  <WhiteList xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <a:string>RowLimit</a:string>
    <a:string>SortList</a:string>
    <a:string>StartRow</a:string>
    <a:string>RefinementFilters</a:string>
    <a:string>Culture</a:string>
    <a:string>RankingModelId</a:string>
    <a:string>TrimDuplicatesIncludeId</a:string>
    <a:string>ReorderingRules</a:string>
    <a:string>EnableQueryRules</a:string>
    <a:string>HiddenConstraints</a:string>
    <a:string>QueryText</a:string>
    <a:string>QueryTemplate</a:string>
    <a:string>SelectProperties</a:string>
    <a:string>SourceID</a:string>
  </WhiteList>
</QueryPropertiesTemplate>
#>

2. Create QuerySuggestion.js java script file. The java script will utilize AutoComplete interaction from jquery-ui and call Search Query REST to populate search results in the AutoComplete drop down.

$(function () {
    // If Anonymous Users
    if (_spPageContextInfo.systemUserKey === undefined) {
        var siteurl = _spPageContextInfo.webAbsoluteUrl;

        // Get all search input boxes
        $("input[id*='_csr_sbox']").each(function () {
            var searchinput = $(this);
            searchinput.data("old_value", searchinput.val());

            // Bind Cut, Paste and KeyDown Event Listener to current search input box
            searchinput.bind("paste cut keydown", function (e) {
                var that = this;
                setTimeout(function () {
                    // If search input box not empty
                    if ($(that).val()) {
                        $(that).data("old_value", $(that).val());

                        // Query Suggestion using AutoComplete
                        searchinput.autocomplete({
                            minLength: 0,
                            source: function (request, response) {
                                // Call Search REST API and get search result for query suggestion
                                $.ajax({
                                    url: siteurl + "/_api/search/query?querytext='" + $(that).val() + "*'&QueryTemplatePropertiesUrl='spfile://webroot/queryparametertemplate.xml'",
                                    method: "GET",
                                    headers: { "Accept": "application/json; odata=verbose" },
                                    success: function (data) {
                                        response($.map(data.d.query.PrimaryQueryResult.RelevantResults.Table.Rows.results, function (item) {
                                            return {
                                                fields: getFields(item.Cells.results)
                                            }
                                        }));
                                    },
                                    error: function (data) {
                                        alert("Query Suggestion Error: Fail to call Search REST API");
                                    }
                                });
                            },
                            focus: function (event, ui) { // If Query Suggestion get focused
                                searchinput.val(ui.item.fields.Title);
                                return false;
                            },
                            select: function (event, ui) { // If Query Suggestion get selected
                                searchinput.val(ui.item.fields.Title);
                                return false;
                            }
                        })
                        .data("ui-autocomplete")._renderItem = function (ul, item) { // render Query suggestion with <ul> and <li>
                            return $("<li></li>")
                                .data("ui-autocomplete-item", item)
                                .append(item.fields.Title)
                                .appendTo(ul);
                        };
                    }
                }, 200);
            })
        });
    }
});

// getFields function: to return all properties to look like json result
function getFields(results) {
    r = {};
    for (var i = 0; i < results.length; i++) {
        if (results[i] != undefined && results[i].Key != undefined) {
            r[results[i].Key] = results[i].Value;
        }
    }
    return r;
}

3. Place javascripts and css in Style Library Root Web:
– jquery.js to /Style Library/QuerySuggestion/js/jquery.js
– jquery-ui.min.js to /Style Library/QuerySuggestion/js/jquery-ui.min.js
– QuerySuggestion.js to Style Library/QuerySuggestion/js/QuerySuggestion.js
– jquery-ui.min.css to Style Library/QuerySuggestion/css/jquery-ui.min.css
4. Update and Publish custom MasterPage to include below: (under existing jquery script element)

<script src="/Style Library/CNP.CWE.QuerySuggestion/js/jquery.js" type="text/javascript"></script>
<script src="/Style Library/CNP.CWE.QuerySuggestion/js/jquery-ui.min.js" type="text/javascript"></script>
<script src="/Style Library/CNP.CWE.QuerySuggestion/js/CNP.CWE.QuerySuggestion.js" type="text/javascript"></script>
<link href="/Style Library/CNP.CWE.QuerySuggestion/css/jquery-ui.min.css" rel="stylesheet" type="text/css"/>

I’ve got the PowerShell and Javascripts from my reference below with bit of modification from me.
QuerySuggestion

References

SharePoint 2013 Hosted App (Part 1) – Configure Apps for SharePoint 2013

SharePoint Hosted App series:

The first part of my SharePoint Hosted App series and this step is required to develop SharePoint Apps in SharePoint 2013. I don’t want to provide you detail steps in this article because I think Gaurav Mahajan & Ahmed Farag has excellent article about it and there is also further explanation from Chris Whitehead technet article. Please check those article out, follow those steps and your environment should be setup successfully.

At a high level the configuration steps are as follows:

  • Infrastructure Configuration
    1. Determine your App Domain.
    2. Configure domain name in DNS.
    3. Create a new wildcard SSL certificate.
  • Farm Configuration
    1. Create SharePoint Service Applications and enable services.
    2. Configure App settings in SharePoint
  • ·App Auth Configuration
    1. Configure SharePoint for low-trust Apps
    2. Configure SharePoint for high-trust Apps

Stay tuned for the next part of the SharePoint Hosted App series.

References

SharePoint Farm Ready in an Hour (Azure)

Today, I found SharePoint Server Farm package in Azure. This is great news for ALL OF US that has Azure account.Then as usual, I tried to quick googling it and found few people already blog about it.
Gee … Where am I for the last month ? Oh well, I guess I’m busy with relocation and new job.

The good thing is I can just them as reference and don’t have to blog all those steps again. Find great blog by Keith Mayer and Melissa Coates in my References sections below.

SharePointAzure

 

References

 

 

%d bloggers like this: