Saturday, May 30, 2009

Using Document Libraries as Template Libraries


Download the source / wsp at CodePlex

Update June 1st: Version 1.5 released. The new version no longer requires a webpart, nor a lot of configuration, so I've updated the outlines seen in this post. See this new post for more info on the update.

Background

SharePoint document libraries are capable of handling many day-to-day tasks, right out of the box; there's no question about that. When you approach the need or wish for simple to maintain template libraries, that's an entirely different story.

A usual approach is to configure content types, and a set of hard-to-maintain URL links between these and actual documents in other locations. Administrative tasks, such as adding and removing templates, quickly becomes next to impossible, as you'll have to create new content types, and add /remove these manually for each document library you want the templates available in.

Another issue is the fact that the "New" menu for document libraries will show all content types at the root level. You can't categorize them in submenus, so large amounts of templates will be everything but a smooth ride for the end user.

Fixing things with a simple feature

Realizing that the aforementioned widespread pain is completely unnecessary, I set out yesterday to write a feature which can:

  • Connect any document library to another document library, with one serving template documents to the other.
  • Not require any administration of new templates, other than uploading them to the template library - Instantly making them available to all document libraries linked with the template library.
  • Allow document libraries and template libraries to reside in different sites.
  • Make the templates instantly, and hierarchically, available from the "New" menu in the document library.

.. And make it all available to all of you, complete with source code, pre-built solution/feature, instructions and documentation.

Installation and Usage

Download the latest source and install packages here.

  1. Unpack and run the install.bat file from the root folder. This will install the solution, and deploy it to all site collections. If you want more fine-grained control, feel free to install and deploy the .wsp-file on your own.
  2. Open a SharePoint portal with document libraries you want to template enable.
  3. Open the Site Settings, and navigate to the Site Feature page.

  4. Enable "Grep's SmartDocument Template Library Connector"

  5. Open a document library you want to expose templates in, and open it's settings.

  6. Navigate to the "Template Library Connector settings" entry, under "General Settings".

  7. Make sure you select "Yes" to enable template library connections, pick a document library to act as a template library, and save.

  8. Navigate back to the document library, and verify that the "New" menu is wired up properly.

  9. Adding new documents to the selected template library will instantly make new templates available from the "New" menu.
  10. You can optionally add a column called "Description" to the template document library, for additional text under each template, in the "New" menu view. If you wish to call this something other than "Description", the column name can be changed through the webpart settings shown above.

And that should be it. If you've got any questions or problems, feel free to drop me a note through the comments here.

Getting Technical

Writing a solution like this takes a few steps, and a bit of tinkering with undocumented SharePoint libraries. Here's a rough cut of what's covered in the source:

  • Utilizing delegate controls to install handlers for all opened pages
  • Find all ListViewWebParts on the webpart page, and retrieve entries from its toolbar.
  • Adding items to ListViewWebPart's menus
  • Adding submenus to the "New" menu
  • Adding submenus to submenus
  • Utilizing the default SharePoint document template JavaScripts to create new documents
  • Retrieving hierarchies from SharePoint document libraries
  • Writing and exposing list settings pages

A few of these steps, such as adding menus and submenus, are powered by extension libraries I've included in the source code. A simple thing like adding a submenu to a submenu isn't easily and readily available in the SharePoint object model, but with the extension libraries in the source project, it's ridiculously simple!

Code Pieces

I won't be covering everything here, but some key parts of the functionality, which I haven't seen elsewhere on the web, will be brought forward.

Extending the LiewViewWebPart's "New" menu

The ListViewWebPart has a toolbar, but this isn't immediately accessible from its object model. Why this is so is oblivious to me, as I see many possible benefits of extending the default views.

Ignoring the ListViewWebPart's attempt at hiding it, and the fact that even toolbar access functions are internal, I extended the System.Web.UI.Control class to give me the following edge:

  1: public static T FindChildByType<T>(this Control self)
  2:     where T : class
  3: {
  4:     var stack = new Stack(self.Controls);
  5:     while (stack.Count > 0)
  6:     {
  7:         var control = stack.Pop() as Control;
  8:         if (control is T)
  9:         {
 10:             return control as T;
 11:         }
 12:         foreach (Control child in control.Controls)
 13:         {
 14:             stack.Push(child);
 15:         }
 16:     }
 17:     return null;
 18: }


The approach is pretty straight forward: it will do a stack-based iteration of the control class, until a control of the target type is found.

Including this allows you to call a function such as FindChildByType() on any class deriving from System.Web.UI.Control. Doing just that on the ListViewWebPart will, with no further hassle, let you access its inner NewMenu.

Once you get an instance of the NewMenu class (which derives from the superbly named ToolBarMenuButton class), you can call functions such as AddMenuItem and AddMenuItemSeparator (see MSDN). There's however no function to add submenus (or submenus to submenus, for that matter), so that will require another extension.

  1: public static SubMenuTemplate AddSubMenuAt(this ToolBarMenuButton self, int index, string id, string displayName,
  2:                                            string imageUrl, string description)
  3: {
  4:     SubMenuTemplate child = CreateSubMenu(id, displayName, imageUrl, description);
  5:     self.MenuTemplateControl.Controls.AddAt(index, child);
  6:     return child;
  7: }


The key part in the above code is the call to CreateSubMenu, which returns a SubMenuTemplate. The latter is a contruct from the Microsoft.SharePoint.WebControls namespace, which is used for several menus around the SharePoint UI, such as the Site Settings menu you get in MOSS when the "Office SharePoint Server Publishing" feature is enabled.

  1: private static SubMenuTemplate CreateSubMenu(string id, string displayName,
  2:                                              string imageUrl, string description)
  3: {
  4:     var child = new SubMenuTemplate();
  5:     child.ID = id;
  6:     child.Text = displayName;
  7:     child.Description = description;
  8:     if (string.IsNullOrEmpty(imageUrl))
  9:     {
 10:         imageUrl = "/_layouts/images/MenuNewItem.gif";
 11:         child.ImageUrl = imageUrl;
 12:     }
 13:     else
 14:     {
 15:         if (imageUrl.IndexOf("/") == -1)
 16:         {
 17:             imageUrl = "/_layouts/images/" + imageUrl;
 18:         }
 19:         child.ImageUrl = imageUrl;
 20:     }
 21:     return child;
 22: }


With these functions, along with a few more included in the source project, such as extending SubMenuTemplate to allow addition of nested submenus; adding menus and submenus is easy as pie.

Adding meaningful menu items

Getting to add menu items is one thing, but actually adding something meaningful is what obviously makes it useful. In this webpart, the goal was to make it possible to instantiate document (templates) from other document libraries, and that turns out to be surprisingly simple if you study how Microsoft does regular template instantiation. The javascript function called "createNewDocumentWithProgID" is the key here, and it accepts such parameters as source file url and target folder url. If the menu items are passed calls to this javascript in its click handler, with the correct urls, document instantiation is in the bag as well.

In Closing

So that's about it. Feel free to check the code, use the webpart, develop it further and share alike. For any questions or comments, use the comment feature on this blog.

Monday, May 18, 2009

Effective SharePoint UI prototyping using jQuery, FireBug and Greasemonkey

Demo script download available at the end of the post.
Updated May 19th, with clearer code examples.

Background

Customizing SharePoint's user interface, or even updating your own interfaces, can be a tiresome thing to do. Usually you'll have to deal with multiple master pages, many master page overrides, multiple css files and a wide array of dynamic code from XSLTs or web parts. All this makes it quite difficult to maintain a clear understanding and overview of the UIs you're customizing, or what the impact of your changes will be.

To add to the already high stress levels, many changes - especially those where you're deploying changes to your own web parts - will require recompiles, iis resets, feature reactivation or similar time consuming steps.

In this article I want to propose an alternative approach to UI changes, using jQuery prototyping. While jQuery isn't necessarily something you'd want to use to do all SharePoint UI restructuring, it does come in handy if you want to make simple runtime enhancements to the current UI, or even want to extend your custom parts with simple asynchronous behavior, or neat visual effects.

To accomplish this, in a way which will require no recompilation, redelpoyment or other nasty things, and overall keep the time between change and prototype visualization as low as possible; we'll be using the FireFox addons FireBug and Greasemonkey. The former is a brilliant DOM / CSS / Html / Network explorer, built into FireFox' interface, with lightning quick structure inspection and navigation. Greasemonkey, on the other hand, can inject custom javascript into any loaded page, without making any serverside changes.

The combination of these tools will allow us to write advanced javascripts in our favorite javascript editor, and see the results immediately, without having to upload any files to the server. A simple browser refresh will suffice.

Requirements
  • FireFox (3.0.10 is the latest at the time of writing)
  • FireBug
  • Greasemonkey
  • Knowledge of JavaScript and HTML / DOM.
Installing
  1. Download and install FireFox, if you haven't done this already.
  2. Start FireFox, and open the Addons config screen, available from the Tool menu.
  3. Activate "Get Addons" function on the top bar, then search for and install FireBug and Greasemonkey.
  4. Restart FireFox.
  5. Navigate to a SharePoint portal you wish to prototype, and confirm that you've got a set of FireBug / Greasemonkey icons in your browser status bar, such as:
  6. Right click the Greasemonkey icon, make sure it's set to enabled, and click New User script:

  7. Fill in values for the namespace and script title, such as:

    The "Includes" box should be the url to the page you wish to prototype. This can be changed later, so stick with the default value for now. In other words, don't copy this from my example above.
  8. Next you'll be asked to select which editor to use for script editing. This can be any text editor of your choice, such as notepad, or in my case, Visual Studio.
  9. Once the editor selection is completed, it will be opened, you'll see an empty script. To test that you've got it all setup correctly, you can try adding an alert statement, and reload the page.
  10. Next click the FireBug icon, and activate the HTML function:
  11. If you're unfamiliar with FireBug, play around with the Inspect function, to see how various UI elements correspond to the HTML source code. Selecting an element also enables you to immediately see the active CSS styles for said element.

Prototyping something useful

Assignment: Making the Quick Launch on the left collapsible.

"Useful" is relative, I agree, but this is at least example use of how to hotplug jQuery into the mix, and how you can use that to experiment with UI changes you'd later wish to deploy to a master page, or portal page.

From here on we'll concentrate mainly on the script opened in your chosen editor. As of now, all the content should be a commented "UserScript" section - which is good. If you've entered anything else, feel free to remove that now.

So, without further stalling, lets code.

Loading scripts (such as jQuery) at runtime

Greasemonkey lets us inject custom scripts, but rather than messing any more around with it's configuration, we'll do the rest of the script loading with code. Since this is a prototype, and we'll be doing all of the prototyping in FireFox; loading scripts is simple.

The only complicating step, is the fact that Greasemonkey will execute Greasemonkey scripts in a sandbox, isolating certain functionality from the scripts already loaded in the browser. The reasoning here is that Greasemonkey, and its scripts, will execute with greater privileges than those loaded by the browser. To prevent browser-loaded scripts from getting unwanted access from your custom Greasemonkey scripts, and possible do naughty stuff on your system, Greasemonkey forces us to take certain actions to access the stuff outside the sandbox. If you wish to read more about that, search for "unsafeWindow" in your favorite search engine.

I've put together a set of functions, which will load custom scripts in order, and map the objects from these into the namespace available from the Greasemonkey sandbox. The functions looks as follows, and you can go ahead and input these in your prototype script. Reading and understanding them are optional steps -- we'll soon be getting to the interesting stuff.

  1: function mapUnsafeObjects(objectArray) {
  2:     if (objectArray == null) {
  3:         return;
  4:     }
  5: 
  6:     for (var i in objectArray) {
  7:         var objectName = objectArray[i];
  8:         window[objectName] = unsafeWindow[objectName];
  9:     }
 10: }
 11: 
 12: function loadScript(url, objectArray, loadCallback) {
 13:     var js = document.createElement('script');
 14:     js.src = url;
 15:     js.type = 'text/javascript';
 16:     js.wrappedJSObject.onload = function() {
 17:         mapUnsafeObjects(objectArray);
 18:         loadCallback();
 19:     };
 20:     document.getElementsByTagName('head')[0].appendChild(js);
 21: }
 22: 
 23: function loadScripts(scriptArray, completeCallback) {
 24:     if (scriptArray.length == 0) {
 25:         completeCallback();
 26:     }
 27:     var scriptEntry = scriptArray.shift();
 28:     loadScript(scriptEntry.url, scriptEntry.objects, function() { loadScripts(scriptArray, completeCallback); });
 29: }


So these are the only utility functions you need to load external scripts. You can reuse them in any prototyping scripts you feed into Greasemonkey, to load whatever external content you need.

Here's an example on how to load the jQuery, jQuery UI and JSON2. We won't be needing more than the first in the following prototype, but if you wish to play around with jQuery UI (which has some cool drag and drop features, among other things), or the ajax + JSON functionality in jQuery, the others will be handy.

  1: loadScripts([
  2:              {url: "http://jquery.com/src/jquery-latest.js", objects: ["jQuery""$"]},
  3:              {url: "http://jquery-ui.googlecode.com/svn/tags/latest/ui/minified/jquery-ui.min.js", objects: []},
  4:              {url: "http://www.json.org/json2.js", objects: ["JSON"]}
  5:             ], onLoadComplete);


What this will do, is load one script at a time, making sure it's completely loaded before skipping to the next in line. For each script loaded, an array of objects is mapped into the sandbox namespace, for easy access in the prototype script. Once all scripts are loaded, the callback function named at the end will be called.

First jQuery action

Thus, the onLoadComplete function will be where our action will take place. Once this is called, jQuery will be fully loaded, and available for use. To demonstrate this, create a callback such as follows:

  1: function onLoadComplete() 
  2: {
  3:     $("a").css({ fontSize: '18pt' });
  4: }


Once you save this, along with the previous code snippets, in the Greasemonkey script opened in your chosen browser, and reload the portal, all html anchor elements will get a ridiculous font size. Not too useful, but it at least confirms everything is working properly. If the jQuery syntax is unfamiliar to you, now would be a good time to read up on some basic examples at jQuery.com.

Collapsible Quick Launch

This is when FireBug will come in handy. Writing the code is simple, and Greasemonkey is great, but without a proper tool to navigate the html, we'd be banging heads into walls before long.

With the FireBug window opened, and pointed to "HTML" mode, hit Inspect and select the section just below a main quick launch link, as shown in this screen shot:



Doing this tells us that the quick launch main menu items have IDs such as "zz2_QuickLaunchMenunN", where N is the menu item index. Tapping into this with jQuery is pretty simple, with a selector such as $("tr[id^=zz2_QuickLaunchMenun]"). What we'd like to do with this, is to attach a click handler to collapse / expand the table row below it, if the following table row isn't another main menu item.

Adding such a handler, in a pretty straight-forward manner, would look like:

  1: function onLoadComplete() {
  2:     $(function() {
  3:         $("tr[id^=zz2_QuickLaunchMenun]")
  4:             .next("[id=]").toggle()
  5:             .prev().click(function(evt) {
  6:                 $(this).next().toggle();
  7:                 evt.preventDefault();
  8:             });
  9:     });
 10: }


This function will install a handler to be run once the DOM tree is loaded and ready for manipulation: the "$(function(){})" syntax. This handler will, noted by line number:
3
Select all tr nodes with an id beginning with "zz2_QuickLaunchMenun". Hence forth called "main items".
4
For each of these main items, select the next sibling, if it has an empty id attribute. The empty id is an important point, as we don't want one main item to collapse the following main item, in the case of there being no child menu between the two. If there's no child menu, lines 5-8 will do nothing more for this main item. In this case, the selector from line 3 will bring us to the next main item instead.
4
Toggle (collapse) the child menu.
5
Select the sibling's previous item, which brings the selection state back to the main item
5
Install a click handler to the main item.
6
Use the toggle() function from jQuery to collapse or expand the sibling (child menu).
7
Simply prevents the click handler from bubbling further on to other nodes down the tree.
If jQuery is still new to you, you may just have noticed that jQuery selectors are cascading. Selecting one node, then doing another selector expression on that, will make a relative selection. That's how we, in the previous example, can move from the parent to the child, then back to the parent.

Saving and reloading the page, should confirm that the above code is working. When you click the background of the main quick launch items, the section will collapse or expand.

To wrap things up, we're going to add another piece of UI code, to animate the font size of the quick launch menu items, as they are mouse hovered. Expand / replace the function to resemble the following:

  1: function onLoadComplete() {
  2:     $(function() {
  3:         $("tr[id^=zz2_QuickLaunchMenun]")
  4:             .mouseenter(function(evt) {
  5:                 $(this).find("a").animate({ fontSize: '14px' });
  6:             })
  7:             .mouseleave(function(evt) {
  8:                 $(this).find("a").animate({ fontSize: '11px' });
  9:             })
 10:             .next("[id=]").toggle()
 11:             .prev().click(function(evt) {
 12:                 $(this).next().toggle();
 13:                 evt.preventDefault();
 14:             });
 15:     });
 16: }


Saving the script and reloading the page will actually uncover a bug in our previous selector, which wrongfully assumed that only the main items of the quick launch menu have the previously mentioned IDs. As it happens, sub items also employ this ID. While the click handler won't be affected (due to the sibling check), the mouseenter and mouseleave hover effect will be. Hovering the menu will now animate everything, while we want only the main items to be affected.

Pulling FireBug back up real quick will show that the items we actually want to attach to, have a parent table with an ID of "zz2_QuickLaunchMenu". To fix the issue we can thus take advantage of a more specific jQuery selector syntax: $("#zz2_QuickLaunchMenu > tbody > tr[id^=zz2_QuickLaunchMenun]").

This tells jQuery to look for a tr node with an id which starts with "zz2_QuickLaunchMenun", that has a direct parent element of type tbody, which in turn has a direct parent element with id "zz2_QuickLaunchMenu". Saving and reloading the page will confirm that the hover effect now only affects the main menu items.

Where to go from here

What I've demonstrated is a way to install and setup a hotpluggable SharePoint UI development environment. jQuery, and various other scripts, have vast possibilities for expanding SharePoints UI, and essential to exploring these, is a way to doing so quickly, without redeploying a bunch of stuff to the development environment (possibly polluting that, with nonsensical experiments).

Once you're done with the prototyping, you would obviously have to deploy jQuery, or whatever scripts you use, to the portal. Either in shape of a master page change, a new aspx page, web parts page template or similar. How you should deploy it depends largely on what you're installing, and what you want it to override.

There's no doubt in my mind that SharePoint development can benefit from such hotplug prototyping, however. Both the previously blogged about CodeConsole web part, and this simple prototyping environment, can - if used with care - simplify your life as a SharePoint developer or designer. Any opinions on how it could be done differently, are much obliged of course!

Downloads



MyProtoType.js

The final example code, with a decent looking expander icon next to the collapsible rows.

CodeConsole Minor Update

The CodeConsole Web Part just got a minor update, to allow simple inclusion of other assemblies and namespaces. This came in handy when I was debugging some SPContext-dependent functionality in a few libraries I deployed to a portal's bin dir.

In the new version, the source / wsp of which is still available here, the syntax is updated to support '@' prefixing for namespaces, and '#' for assemblies. Such as:

@MyLibrary
#Some.NameSpace.From.MyLibrary
MyClass foo = new MyClass();
output.Write(foo.SomeFunction());


Where the first MyLibrary reference would be an assembly in e.g. the bin folder of the current web application, or the GAC. No .dll suffix required.

Wednesday, May 13, 2009

CodeConsole Web Part for SharePoint

About the CodeConsole Web Part
Download Source Code + WSP

This is a web part I've found myself wanting several times, when I've been either too lazy to compile and deploy code, or haven't had the development tools available on the target SharePoint box.

In short, the CodeConsole Web Part ..
  • Executes C# code, entered into a text area, on the server. The code can access the SharePoint object model, and other .NET / ASP.NET objects.
  • Displays output from the execution in an output text area.
  • Uses jQuery to make asynchronous requests to a custom .asmx service on the server.
  • Uses JSON to serialize / deserialize data, should more complex structures be required by someone who decides to use it.
A Screen Shot of the Web Part in action:

CodeConsole Screen Shot
Click the image for a larger version
Update: Uploaded a demonstration screen capture video, in case a screen shot isn't enough. Check it out.

In this shot, the input frame is white, and the output frame is black. The code passed through the input box will have the following using directives available to it by default:
  • using System;
  • using System.IO;
  • using System.Text;
  • using Microsoft.SharePoint;
This can easily be extended in the attached code, otherwise you'll have to use fully qualified names in the code.

For output redirection, the code entered will have an object called 'output' available. This is a System.IO.TextWriter, meaning it will have such functions as Write and WriteLine. The above example screen shot uses this output object, so turn to that if you're confused.

Installing the web part is pretty straight forward, but will (due to the jQuery / JSON use) involve a few manual config steps. See my previous blog post for more information on that. Within the source code package, there's a folder called "wsp", within which you'll find a precompiled solution file, which can be deployed to a working SharePoint 3.0 portal.

About the implementation

The code isn't complex at all, and I'm sure many would point out that I could have done things a little bit differently here and there (such as rewriting how the web part is displayed, and taking optional configuration input to decide whether or not to link the json / jquery javascripts - which may not be necessary, if they've already been imported by another webpart, or in the master page).

Essential files all reside in the TEMPLATE\FEATURES\CodeConsoleWP folder, where:

CodeConsole.cs is the web part's source file. This emits all ui and javascript, including references to jQuery and JSON. Most essential of which is the following javascript. Yes, it's simple. Feel free to extend it. I'm sure intellisense would be a nice addition ;)


$(function(e){
var input = $("#CodeConsole_input");
var output = $("#CodeConsole_output");
$("#CodeConsole_execute").click(function(be) {
output.text("Compiling, please wait.");
var code = {'code' : input.text()};
var jsonStr = JSON.stringify(code);
$.ajax({
type: 'POST',
url: '/_vti_bin/CodeConsoleSVC.asmx/ExecuteCode',
data: jsonStr,
contentType: 'application/json; charset=utf-8',
dataType: 'json',
success: function(msg) { output.text(msg.d); },
error: function(xhr, msg) { output.text("Ajax Error:\n" + msg + "\n" + xhr.responseText); }
});
});
});


CodeConsoleSVC.cs is the web service source file, which basically looks like:


namespace CodeConsoleWP
{
using System;
using System.IO;
using System.Web.Script.Services;
using System.Web.Services;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class CodeConsoleSVC : WebService
{
[WebMethod]
public string ExecuteCode(string code)
{
using (TextWriter stringWriter = new StringWriter())
{
try
{
Dynamic.ExecuteCode(code, stringWriter,
new[] {"System", "System.Text", "System.IO", "Microsoft.SharePoint"});
return stringWriter.ToString();
}
catch (Exception ex)
{
return "Error:\n" + ex.Message;
}
}
}
}
}


Most interesting in the above web service, which will be deployed to the /_vti_bin/, along with other SharePoint services, is the call to Dynamic.ExecuteCode. This is a very simple class I typed up, which generates an assembly in-memory, referencing all assemblies of the hosting assembly (the web part, meaning SharePoint assemblies and such will be referenced), wrapping the code, executes it and returns an optional return value.

Dynamic.cs


internal static class Dynamic
{
public static object ExecuteCode(string code, TextWriter output, string[] namespaces)
{
using (CodeDomProvider provider = CodeDomProvider.CreateProvider("C#"))
{
var parameters = new CompilerParameters
{
GenerateExecutable = false,
IncludeDebugInformation = false,
MainClass = "temp",
GenerateInMemory = true
};

foreach (AssemblyName refasm in Assembly.GetExecutingAssembly().GetReferencedAssemblies())
{
parameters.ReferencedAssemblies.Add(Assembly.Load(refasm).Location);
}
string usingDirectives = "";
foreach (string ns in namespaces)
{
usingDirectives += "using " + ns + ";";
}
CompilerResults results = provider.CompileAssemblyFromSource(
parameters,
@"namespace Dynamic {" +
usingDirectives +
@"public class Code { public object Run(System.IO.TextWriter output) { " +
code +
"; return null; } } }");

if (results.Errors.Count > 0)
{
var sb = new StringBuilder();
foreach (CompilerError error in results.Errors)
{
sb.AppendLine(String.Format("{0},{1}: {2}", error.Line, error.Column, error.ErrorText));
}
throw new CompileException(sb.ToString());
}
else
{
object obj = results.CompiledAssembly.CreateInstance("Dynamic.Code");
object ret = obj.GetType().InvokeMember("Run", BindingFlags.InvokeMethod, null, obj,
new object[] {output});
return ret;
}
}
}

public class CompileException : Exception
{
public CompileException(string s)
: base(s)
{
}
}
}


Compiling and installing

Upon build, the source will be compiled, and put along with the service endpoint in a SharePoint solution file (wsp). This is taken care of by the build scripts in the DeploymentFiles folder. This file will end up in the CodeConsoleWP\wsp\ folder. To install the web part, just deploy this solution to the SharePoint server. Be sure to follow the instructions in my previous post, to add json/ajax support to SharePoint, though - otherwise the service call will fail miserably.

If you've got any questions, drop me a note in the comments here, on twitter, or by email.

Download Source Code + WSP

Enabling JSON serialization and jQuery posts for SharePoint web services

While making the finishing touches to a new web part I'm publishing here shortly, I found myself wanting to make JSON enabled jQuery AJAX requests to custom .asmx services I'd deploy to the _vti_bin service root of my SharePoint portal. The incentive is simple; jQuery is quite nice for quickly typing up dynamic UIs, and by connecting that to a service backend, I can do even more fun stuff (which will become clear in my next post). An alternative to jQuery would be to stick with ASP.NET Ajax, but to be honest, I find that a lot less comfortable, when my code is mostly client side (javascript), and I'm deploying it all to SharePoint.

The overhead of using ASP.NET ajax for simple stuff like this is another story altogether, and while I don't have any specific numbers, I'm sure you'd see quite a bit more data on the wire (large scripts, xml encapsulation, etc.), compared to simple jQuery ajax post requests with JSON for data encapsulation. If you're interested in that part, throw in a few google searches - I'm sure it's been thoroughly covered elsewhere.

Anyhoo, the point here is jQuery/JSON and whatnot, so if that's what you're after; keep reading.

What I soon discovered after attempting to make simple $.ajax calls from jQuery, was that HttpPost isn't enabled for the _vti_bin folder. Trying to call /_vti_bin/MyService.asmx/SomeMethod will result in some less-than-intuitive error page (especially when seen through the deserialized error callback of jQuery's .ajax()). This is essentially caused by the default _vti_bin config not allowing HttpPost service calls. Your options are either to change the configuration, which I'll soon outline, or make SOAP envelope calls. I find the latter fairly tedious, having to construct fairly large XML structures in strings, rather than just calling an url like that of SomeMethod above.

Fixing the config, to allow simple service call urls, is pretty simple. Open the web.config from your "Web Server Extensions\12\ISAPI" folder, and flick the "remove" keyword of HttpPost to "add". The system.web configuration should look like:

<system.web>
    <webServices>
        <protocols>
            <remove name="HttpGet" />
            <add name="HttpPost" />
            <remove name="HttpPostLocalhost" />
            <add name="Documentation" />
        </protocols>
    </webServices>
    <customErrors mode="On"/>
</system.web>


At this point, you'll be able to call the web services through HTTP Post requests, even from jQuery, such as:

$.ajax({
    type: 'POST',
    url: '/_vti_bin/MyService.asmx/SomeMethod',
    data: '{}',
    contentType: 'application/json; charset=utf-8',
    dataType: 'json',
    success: function(msg) { alert(msg.d); },
    error: function(xhr, msg){ alert(msg + '\n' + xhr.responseText); }
});


Still there's the case of JSON, however, so the JavaScript above would still trigger the error callback, with your web service's return value encapsulated in XML. Should you wish to use XML for data transport, you'd be all set now.

To enable JSON, you'll first of all need the .NET 3.5 framework installed on the server. If you haven't already done this (2008 should have it pre-installed), go do so now. Once that's in place, there's a few quick changes which need to be made to your web application's web.config - not dissimilar to the general steps to make ASP.NET Ajax working.

Within <system.web><httphandlers> tag, add the following line, right after the initial <remove>:

<add verb="POST" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

Within the <safecontrols> tag, add the following at the bottom:

<SafeControl Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" Namespace="System.Web.UI" TypeName="*" Safe="True" />

This should pretty much be all you need, and you should be all set for using jQuery and JSON to query your own custom _vti_bin deplyed services, from web parts, pages and so forth.

My next post will provide an example of this combination, calling a custom .asmx service from jQuery, with JSON serialization for parameters / return value, neatly wrapped up in a WSP.

Thursday, May 7, 2009

Item level security with SharePoint web services

Or: Deploying WSS like services to SharePoint farms as solutions

While the SharePoint service model (found in /_vti_bin) supports retrieval of List and Web permissions, it supplies no way of fetching permissions for List items.

Implementing such as service is however fairly straight forward, so I typed up a quick example which:
  • Provides a PermissionsEx.asmx service, which is a skeleton for your extended permission service needs.
  • Exposes a demo GetListItemPermissions(listname, itemguidstring) function, which returns an xml structure of all permissions for the list item.
  • Has various helper functions and classes, to easily retrieve assets from the SharePoint instance which hosts the service.
  • Automatically wraps the service, and the additional files, in a .wsp solution. Upon deployment, this will put the service binary into the web app's bin folder, and the service endpoint into the isapi folder (which is where _vti_bin points).
The source can be downloaded here.

There's not a lot of wizardry going on there, so it should be fairly self explanatory - if you're vaguely familiar with solutions and web services as such. I've structured the service in a manner similar to that of the original SharePoint web services, so if you've looked into these before, you should also feel quite at home.

What's worth noting is that if you decide to extend the service, and provide more functions, you should read up on Walkthrough: Creating a Custom Web Service to learn how to generate new discovery and wsdl files. Adapting these files from the generated versions is quite simple, but you'll have to make an extra step for XmlElement return types. Check the GetListItemPermissionCollectionResponse of the PermissionsExwsdl.aspx file to see the structure you'll need. Once new files are generated, you should overwrite those in the project directory.

If you've got any questions, or need help getting it all going, feel free to tweet me @einaros, or drop a comment here.