CHUVASH.eu

CHunky Universe of Vigourous Astonishing SHarepoint :)

Tag Archives: HttpHandler

Web Application Properties as JSON

I saw an interesting question on sharepoint.stackexchange: How to access a Web application/Farm level property bag via jQuery/Javascript/ClientContext. Some time ago I tested a custom http handler, so I wanted to try a custom httphandler for this as well. It worked. Here more details:

Just deploy sp-lend-id.tupsam from the solution. If you don’t have any properties in your web application, just add some:

asnp microsoft.sharepoint.powershell
$app = get-spwebapplication http://dev
$app.Properties.Add("Santa", "Claus")
$app.Properties.Add("Ded", "Moroz")
$app.Properties.Add("Hel", "Muci")
$app.Update()

Then just to test open the httphandler directly in the browser:

http:///_layouts/sp-lend-id/tupsam/properties.ashx

Then add the script to your solution where you need it (customaction, masterpage…) and whenever you want some web application property just use it:

_webAppProperties.Santa

ScriptResx.ashx in SharePoint

In my previous post I showed a little proof-of-concept for an httphandler which I want to use to dynamically get the localization resources from SharePoint as javascript object. But wait a moment. How does SharePoint handle localization on client? When you look in Script tab in Chrome dev tools, you’ll find:

ScriptResource.axd

It is added to the server when deployed. See a good introduction to WebResource.axd and ScriptResource.axd by Brian Chavez.

ScriptResx.ashx

That was new. Try to google it. You won’t find much results.

Wait a sec, so there is actually the localization httphandler in SharePoint which I wanted to do?

Can’t be true, can it? Let’s look closer. So the url for this javascript file is:

/_layouts/ScriptResx.ashx?culture=nb-no&name=SP.Res

It is added with RegisterSod:

<script type="text/javascript">
    RegisterSod("sp.res.resx",
        "\u002f_layouts\u002fScriptResx.ashx?culture=nb\u00252Dno\u0026name=SP\u00252ERes\u0026rev=4rLK\u00252F2Oo4ectujVWgrynrA\u00253D\u00253D");
</script>

It begins by:

_EnsureJSNamespace('SP');
SP.Res={lcid:'1044',autocompleteAccessibleMenuName:'Autofullf\u00F8r-meny'....

Very nice. But where resides this SP.Res?… Perhaps, it isn’t a file, rather a name, that the javascript code is encapsulated within. The javascript object is called SP.Res.

By the way, a nice usage of it is to define the current language:

var lang = SP.Res.lcid;

ScriptResx.ashx file is located in Layouts folder and contains only one line:

<%@ WebHandler
   Language="C#" Class="Microsoft.SharePoint.ApplicationPages.ScriptResxHandler,Microsoft.SharePoint.ApplicationPages,Version=14.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c"
     %>

So the class is Microsoft.SharePoint.ApplicationPages.ScriptResxHandler. Google gives no results… The Microsoft.SharePoint.ApplicationPages.dll resides in C:\inetpub\wwwroot\wss\VirtualDirectories\80\_app_bin

This is what .NET Reflector reveals:

The file is loaded is actually 14/Resources/SP.Res.resx:

Usage for other and custom .resx files

Wow, those who read all this, you’ll be awarded: You can actually load any localization files with ScriptResx.ashx (anything which can be found in 14/Resources/-folder, core, spscore and much more, or your own custom resx-files). Just add your script:

<script src="/_layouts/ScriptResx.ashx?culture=nb-no&name=Takana></script>

This will be loaded into “Res”.

One shortcoming is that multiple resx-files would override each other because every localization file loads properties into “Res” object (except SP.Res). Another shortcoming is that we can’t load culture-invariant localization files, because the filePath concatenation is much hard coded: It depends on a dot and culture-name:

Get Culture dynamically

ScriptResx.ashx depends on culture name. Sure we can put this directly in the script tag. But what if you can’t know the current language of the site? It is not so difficult actually, just get the current culture name in the asp.net markup:

<script
    type="text/javascript"
    src="/_layouts/ScriptResx.ashx?culture=<%= System.Globalization.CultureInfo.CurrentCulture.Name %>&name=Takana">
</script>

Update: An even better solution is to use L_Menu_LCID (found on Ruud Heemskerk’s Blog):

<script
    type="text/javascript"
    src="/_layouts/ScriptResx.ashx?culture=" + L_Menu_LCID + "&name=Takana">
</script>
Resolving name conflict

If you load multiple localization files, all will use “Res” name for the localization object and override the latest localizations. To solve it we must catch the Res object and encapsulate it within another object which we have control over. Here we can use many techniques, among them ExecuteOrDelayUntilScriptLoaded (with RegisterSod and LoadSod) from SharePoint, we can use head.js to load and run a callback. But the simplest way is to use jQuery.getScript function:

jQuery.getScript("/_layouts/scriptresx.ashx?culture=nb-no&name=Takana")
	.done(function() { Takana = {}; Takana.Res = Res; })
	.fail(function() {console.log("error");});
Update 2013-02-01

I found a better way to resolve the name conflict for resx-files loaded with scriptresx.ashx. Just update the classFullName resheader in your resx file. Details in my new post:

JavaScript Localization in SharePoint

Custom HttpHandler in SharePoint for getting dynamic javascript code

Sometimes I want to add some dynamic javascript code. E.g. I had an idea to get javascript code for localization dynamically. Kirk Evan provides a very clean approach to add ASP.NET HttpHandler to SharePoint. This is just a little lab:

Create a SharePoint empty project. I call it Jerkelle.
Create a class called Resx2JsHandler and implement the IHttpHandler interface
Add the mapped folder Layouts and create Jerkelle.ashx file

Here is the project. I probably will publish it on my github account:

The Jerkelle.ashx file is very simple, it references to the code file (Resx2JsHandler.cs):

<%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="Jerkelle, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1140e8620a44557a" %>
<%@ WebHandler Language="C#" Class="Jerkelle.Resx2JsHandler" %>

The code file is nothing advanced neither, for this little lab:

using System.Web;

namespace Jerkelle
{
    public class Resx2JsHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            context.Response.Write("Hello HttpHandler from the site ");
        }

        public bool IsReusable
        {
            get { return false; }
        }
    }
}

So now it is time to deploy! Then when we hit the ashx in the address bar we’ll get the nice message: “Hello HttpHandler from the site”. Isn’t it beautiful?

Allright, so far so good. But the intention of this lab is to create a dynamic javascript file. Well, it doesn’t need to be dynamic for now. Let’s convert it to a javascript source file. In order to quickly test, let’s write a simple alert:

using System.Web;

namespace Jerkelle
{
    public class Resx2JsHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "application/x-javascript";
            context.Response.Write("alert('Hello from ashx');");
        }

        public bool IsReusable
        {
            get { return false; }
        }
    }
}

Now we can run this by adding it to the page as a script. Or just let’s add it with a little script (like we did with jQuery):

var script = document.createElement("script");
script.setAttribute("type","text/javascript");
script.setAttribute("src","/_layouts/jerkelle.ashx");
document.body.appendChild(script);

And it works!

Alright, we go a step further, we create an httphandler which returns the localization strings as javascript:

public class Resx2JsHandler : IHttpHandler
{
    public bool IsReusable
    {
        get { return false; }
    }
    public void ProcessRequest(HttpContext context)
    {
        var res = context.Request.QueryString["res"];
        var js = GetJavascript(res);
        context.Response.ContentType = "application/x-javascript";
        context.Response.Write(js);
    }

    private static string GetJavascript(string res)
    {
        var result = string.Empty;
        if (res == null)
        {
            return result;
        }
        var ci = SPContext.Current != null
                        ? new CultureInfo((int) SPContext.Current.Web.Language)
                        : CultureInfo.CurrentCulture;
        var name = ci.Name;
        var fileName = string.Format("\\Resources\\{0}.{1}.resx", res, name);
        var path = SPUtility.GetGenericSetupPath(fileName);
        var text = GetContent(path);
        var json = ConvertToJson(text);
        result = string.Format("{0} = {1};\n", res, json);
        return result;
    }

    private static string ConvertToJson(string text)
    {
        var json = string.Empty;

        var doc = new XmlDocument();
        doc.LoadXml(text);
        var elements = doc.SelectNodes("/root/data");
        if (elements == null)
        {
            return json;
        }
        var pairs = new List();
        foreach(XmlNode e in elements)
        {
            var name = e.Attributes[0].Value;
            var value = e.FirstChild.InnerText.Trim().Replace("\"", "'");
            value = Regex.Replace(value, "\r?\n", "");
            var pair = string.Format("\t\"{0}\" : \"{1}\"", name, value);
            pairs.Add(pair);
        }
        var s = string.Join(",\n", pairs.ToArray());
        json = string.Format("{{\n{0}}}", s);
        return json;
    }

    private static string GetContent(string path)
    {
        var content = string.Empty;
        SPSecurity.RunWithElevatedPrivileges(() =>
        {
            content = System.IO.File.ReadAllText(path);
        });
        return content;
    }
}

User res query string to get a localization file:

ScriptResx.ashx

EDIT: Later I discovered ScriptResx.ashx: there is already such an httphandler, with its shortcomings but, it is already implemented.

Вула Чăвашла

VulaCV - Чăвашла вулаттаракан сайт

Discovering SharePoint

And going crazy doing it

Bram de Jager - Coder, Speaker, Author

Office 365, SharePoint and Azure

SharePoint Dragons

Nikander & Margriet on SharePoint

Paul J. Swider - RealActivity

RealActivity is a specialized Microsoft healthcare services and solution advisory firm.

Cameron Dwyer

Office 365, SharePoint, Azure, OnePlace Solutions & Life's Other Little Wonders

paul.tavares

Me and My doings!

Share SharePoint Points !

By Mohit Vashishtha

Jimmy Janlén "Den Scrummande Konsulten"

Erfarenheter, synpunkter och raljerande om Scrum från Jimmy Janlén

Aryan Nava

DevOps, Cloud and Blockchain Consultant

SPJoel

SharePoint for everyone

SharePointRyan

Ryan Dennis is a SharePoint Solution Architect with a passion for SharePoint and PowerShell

SharePoint 2020

The Vision for a Future of Clarity

Aharoni in Unicode

Treacle tarts for great justice

... And All That JS

JavaScript, Web Apps and SharePoint

blksthl

Mostly what I know about SharePoint - CommunicoCuspis