Jaap Vossers' SharePoint Blog

Just another WordPress.com site

SharePoint InlineSiteSettings 2010 – improved productivity for Administrators and Developers

leave a comment »

After having released SharePoint InlineSiteSettings for SharePoint 2007 a while ago, and having used a little desktop application called Launchy which is used to start desktop applications using just a few keystrokes, I decided to build an enhanced version of InlineSiteSettings, built for SharePoint 2010 with features similar to Launchy’s.

The end result is SharePoint InlineSiteSettings 2010, which can be downloaded from CodePlex at http://sitesettings2010.codeplex.com/

inlinesitesettings2010

The purpose of the solution is to improve productivity for SharePoint 2010 users who regularly access the Site Settings page, i.e. SharePoint Administrators and SharePoint Developers. It allows them to access the Site Settings in a dialog by pressing Ctrl+s, so no need to move your mouse to Site Actions, click it, click Site Settings, and wait for the full page to load.

As we all know, once the Site Settings page has been loaded, it can actually take a few seconds to spot the link you are looking for (as the links are not listed in alphabetical order), so what’s new in this version of SharePoint InlineSiteSettings is that users can start typing the title of the link they whish to navigate to, and with real-time filtering functionality, all links that do not match your filter will disappear from view. In addition to that, as soon as exactly one link is left that matches your filter, it will automatically redirect you to that page, as can be seen in the demo screencast below. As a result, navigating between administrative pages in SharePoint 2010 will be less painful.

SharePoint InlineSiteSettings 2010 is packaged as a Sandbox Solution, and does not depend on any server side code. The good thing about this is that it works on SharePoint Online (Office365).

Download SharePoint InlineSiteSettings 2010 from CodePlex

Written by jvossers

May 8, 2011 at 9:59 pm

Bypass caching with jQuery AJAX GET requests

with one comment

As I seem to use this trick quite often and I keep forgetting the exact details on how to implement it, I thought it would be good to document this.

Using jQuery, I often make ansynchronous GET requests to a custom ASHX handler in SharePoint’s _layouts folder which returns some data that I want to display. This data is always dynamic, but sometimes the browser tries to cache the results from the previous request, so you might not get the response you expected.

To avoid this, simply make the url for eacht request unique by adding a timestamp to it in javascript.

var url = '/_layouts/MyProject/MyHandler.ashx?unique=' + new Date().getTime();

Written by jvossers

January 11, 2011 at 10:41 am

Malicious Sandbox Solutions in SharePoint 2010 – my private data has been stolen!

with 4 comments

We all know about the existence of Sandbox Solutions in SharePoint 2010 and why you would want to use it. We also know that server side code running in the Sandbox is very restricted. Things like accessing the server’s file system, using the SPSite constructor, sending e-mails, making web requests, plus loads of other actions cannot be performed when running in the Sandbox for the sake of security.

It is easy to start thinking that Sandbox Solutions cannot do too much harm and that any damage done stays within the walls of the Site Collection. The aim of this blog post is to bust this myth and make people aware that they are still responsible for validating the contents of any Sandbox Solution they activate on their Site Collection.

What if I told you I have developed a Sandbox Solution that upon activation collects documents and list data from your site collection and sends it to me, the evil developer, outside your Site Collection walls? Would you be surprised to hear this is possible? I think many people would be.

It is possible.

  1. Evil Dev produces Malicious Sandbox Solution
  2. Site Collection Admin uploads and installs Malicious Sandbox Solution
  3. Malicious Sandbox Solution collects private data from Site Collection and sends its to Evil Dev

malsandbox

Now it’s time to see it in action.

For obvious reasons I will not post the full source code, but I am happy to explain how it works.

I am assuming all of the code runs under the context of a Site Collection Administrator.

Note that the following “solution” does NOT depend on the fact that it runs in a Sandbox. In fact, as Jan Tielens has kindly pointed out, the same result could be achieved with a JavaScript only solution.

Step 1 – Save site as a template through JavaScript in the background
Let’s leverage the “Save as Template” functionality as used in /_layouts/savetmpl.aspx to produce a WSP that contains the site with all its contents, including documents in document libraries and lists with data. The first hurdle we need to get past is to figure out how we are actually going to achieve this, since SPSolutionExporter.ExportWebToGallery() is not available in Sandbox Solutions. One way to hack around this is to use jQuery to perform an AJAX POST to /_layouts/savetmpl.aspx providing all the POST parameters that it expects. Remember, HTTP is stateless. We need to perform an initial AJAX GET to /_layouts/savetmpl.aspx to obtain the values of __REQUESTDIGEST, __VIEWSTATE and__EVENTVALIDATION so we can trick ASP.NET into thinking that the POST we are about to perform originates from a user looking at /_layouts/savetmpl.aspx in his browser. All the other post parameters that the page expects to receive as part of a POST can be obtained using Fiddler as we perform a manual form submit through the browser on /_layouts/savetmpl.aspx
All of our JavaScript code will run on a custom Web Part page that is loaded inside an iframe, except for the bit of JavaScript that is responsible for creating this iframe. This Web Part page plus the custom Web Part that lives on this page have been deployed to our Site Collection using Features that reside in our malicious Sandbox Solution. A Feature containing a CustomAction with Location=”ScriptLink” takes care of loading our JavaScript file on every page.
Once we have the JavaScript in place to initiate the creation of the template, we need to wait for the process to complete. Lucky for us, the /_layouts/savetmpl.aspx page blocks until it has finished producing the template, so we know that when the callback of our AJAX POST is called we are ready to rock. In the next step I will explain why it’s important for the script to be informed when the page has finished creating the template.

Step 2 – Get access to saved template data in JavaScript code
We have successfully managed to save the template in the Solution Gallery of the Site Collection. Now we need to access this file. I mean really access the file contents in JavaScript code. Unfortunately we can’t pass around binary data in JavaScript, so we need an alternative. Wouldn’t it be cool if we had access to a JavaScript string variable containing a base64 encoded string that represents the binary data of our freshly baked template? Yes, that would be very cool. The Web Part that lives on the Web Part page can do this for us. As part of the page lifecycle, the Web Part looks to see if the expected template file exists in the Solution Gallery (the Solution gallery is just an SPList and the template file we are looking for is just an SPFile). If the file exists, it gets the binary data of it, converts it to a base64 encoded string and renders a snippet of JavaScript that defines the string variable we are going to use in step 3. The first time the Web Part page was loaded inside the iframe the template did not exist in the Solution gallery, hence it did not render this JavaScript variable, which was also a hint to our JavaScript to initiate the creation of the template. Remember we said we have a callback in our script to notify us when /_layouts/savetmpl.aspx has completed producing our template? Excellent – because inside our callback we are going to force our iframe to refresh, causing the Web Part to output the base64 encoded string variable.

Step 3 – Send template data to a remote server
Now what we have our data available in JavaScript, how do we “send” this to the outside world? Perfoming AJAX calls to anything that is not on the same domain will be noticed or even blocked by the browser, so this is not an option. This is not true for regular form posts. All we need to do is create an html form, set its action attribute to point to a “collector page” somewhere on the internet. This page can then listen for incoming form posts and transform the posted base64 encoded string back to its binary representation and save it somewhere as an actual file. Your data has just been stolen…

To conclude
I don’t think there is anything technically wrong with the security model of Sandbox Solutions. The point I am trying to make with this blog post is that even though there is a lot of stuff that cannot be done from within a Sandbox Solution, there is still quite a lot of stuff that can be done -which can be seen as good or bad!

Do not blindly trust a Sandbox Solution. As Site Collection Administrators, we are still responsible when it comes to assessing and validating the trustworthiness of a Sandbox Solution and its source.

What about custom Solution Validators? You could use these to only allow certain pre-approved or signed Sandbox Solutions to be activated for example, but it comes at a price. You compromise on business agility in order to increase security. Kind of reminds me of Farm Solutions…

Written by jvossers

November 12, 2010 at 9:34 am

Minimal implementation of a custom templated ASP.NET control using the ITemplate interface

with one comment

Below you will find a code snippet to demonstrate a minimal implementation of a custom ASP.NET control that allows page designers to specify a template which will be used by the control to render itself, using the ITemplate interface.

Markup:

   1:  <my:MyTemplateBasedControl runat="server" id="myTemplateBasedControl1">
   2:      <MyHeaderTemplate>
   3:          <div>
   4:              Some text <asp:Button runat="server" Text="And a button..." />
   5:          </div>
   6:      </MyHeaderTemplate>
   7:  </my:MyTemplateBasedControl>

Control code:

   1:  public class MyTemplateBasedControl : CompositeControl
   2:  {
   3:      public ITemplate MyHeaderTemplate { get; set; }
   4:   
   5:      public MyTemplateBasedControl()
   6:      {
   7:   
   8:      }
   9:   
  10:      protected override void CreateChildControls()
  11:      {
  12:          base.CreateChildControls();
  13:   
  14:          // If <MyHeaderTemplate></MyHeaderTemplate> was supplied
  15:          // in the page that contains this control, then this.HeaderTemplate
  16:          // will automatically be set for you with an object of type
  17:          // System.Web.UI.CompiledTemplateBuilder
  18:          if (this.MyHeaderTemplate != null)
  19:          {
  20:              // let's instantiate multiple instances of our template
  21:              for (int i = 0; i < 10; i++)
  22:              {
  23:                  // This will "convert" the string supplied inside
  24:                  // <MyHeaderTemplate></MyHeaderTemplate> into 0 or more controls.
  25:                  // Each of these controls will be added to this.Controls
  26:                  // since we pass in "this" into the InstantiateIn() method
  27:                  this.MyHeaderTemplate.InstantiateIn(this);
  28:              }
  29:          }
  30:      }
  31:  }

Result:

itemplate_result

Written by jvossers

May 12, 2010 at 3:35 pm

Posted in ASP.NET, Development

Released: SharePoint WebPartSlices

with one comment

I have recently released my sixth CodePlex project (list of all my CodePlex projects), titled SharePoint WebPartSlices.

To all the jQuery fans – Sorry, no jQuery this time! SharePoint WebPartSlices is a server-side solution that allows users to transform all Web Parts on a page into IE8 Web Slices simply by adding the webslices=1 querystring parameter to the url of the page containing the web parts to be transformed, basically allowing you to add web parts to your IE8 Favorites Bar.

webpartslices1

Voilà – No need to navigate to the original page to see the updated Web Part.

webpartslices2

Installation consists of a WSP deployment + Feature activation only.

SharePoint WebPartSlices on CodePlex

Written by jvossers

February 4, 2010 at 12:06 pm

Per-Location View Settings in SharePoint 2010 (Views per Content Type)

with 9 comments

What’s that supposed to mean? That’s what went through my head when I saw a new link which had appeared on the List Settings page (listedit.aspx) in SharePoint 2010.

When I clicked the link, I was directed to a page where I could manage the available views per “location”.

The word “location” can have many different meanings. My initial thought was that it was referring to folders inside the list and that this page is used to configure which views are available per folder.

This turned out to be correct. I added a folder to the list and was able to make my custom view appear on there, whilst hiding it from the root of the list.

Is that all? Why call it locations when you mean folders? Well, because it does not only apply to folders!

You can configure available views for ANY NODE in the “Metadata navigation”. As a result, In addition to views per folder – depending on how you set up your Metadata navigation for your list – you can:

  • Define views that are available only on items of a particular Content Type (my favourite, demonstrated below)
  • Define views that are available only on items that have a particular value for a field of type single-value choice.
  • Define views that are available only on items that have a particular term applied to it on a field of type Managed metadata.

Let me demonstrate how to make our “Books Grouped by Author” view available only to our Book content type in our Products list, whilst hiding it for all other type of products in the list.

Below is a summary of all the steps. I will only discuss the last two steps where we configure Metadata navigation and the Per-location view settings, as this what this article is all about.

  1. Create custom list Products
  2. Create content types:
    1. Book (Title, Price, Author)
    2. Movie (Title, Price)
    3. Music Album (Title, Price, Artist)
  3. Configure list to allow management of Content Types
  4. Associate Book, Movie and Music Album with list and delete Item Content Type
  5. Populate list with items for each Content Type
  6. Create view Books Grouped by Author
  7. Configure Metadata navigation
  8. Configure Per-location view settings

Once we have successfully performed steps 1 to 6, we need to bring up the Metadata navigation settings screen for our list. The link to this page can be found on the List Settings page. In the Configure Metadata Hierarchies section, we need to select the “Content Type” item from the list on the left and move it to the right and press OK

As a result, we should now get a hierarchical navigation control on the left of our list.

Once that’s done, we need to bring up our Configure per-location view settings page. The link to this page can also be found on the List Settings page. On the left, there is a hierarchical control labelled “Location to Configure”. We need to use this to select the node (or Location if you like) to which we will be applying the configuration defined on the right. We start with the root node, which is selected by default. We don’t want our grouped view to be available at the root, so we select it in the “Views available at this location” list and move it to the “Views hidden from this location” list and press Apply. Next, we need to expand the “Content Type” node in the tree on the left and select Book. By default it is configured to inherit its settings from its parent, which it not what we want. Set this to No, and move the grouped view to the “Views available at this location” list. While we are at it, let’s move the All Items view to the “Views hidden from this location” list, so that our grouped view becomes the default view for the Book Content Type, and press OK.

Navigate to the list. When selecting Book in the Metadata Navigation on the left, the grouped view should now show instead of the All Items view. Note that  the Book Content Type node in the metadata navigation, is also the ONLY location where our grouped view is available.

On a final note, I noticed that the Per-location view settings link does not appear on the List Settings page when the list is contained in a Blank Site. I performed the actions above on a Team Site. Presumably certain Features needs to be activated to enable this functionality, however I currently don’t know which one. Also, I am not sure how much of this functionality depends on SharePoint Server 2010 and whether it will work on a SharePoint Foundation installation.

Written by jvossers

December 27, 2009 at 10:13 pm

Posted in SharePoint 2010

jQuery Developer Frameworks for SharePoint

with 2 comments

If you are into developing SharePoint solutions using jQuery, you should really have a look at the following GREAT CodePlex projects. Both projects have been developed by real experts in the SharePoint & jQuery field 🙂

jPoint – Developed by a team lead by Will Lawrence (@willhlaw)

jPoint is an open source javascript API and script deployment framework for Sharepoint. It leverages the speed and agility of jQuery to manipulate Sharepoint forms and pages, communicate to Sharepoint webservices, and to build jPart plugins.

jQuery Library for SharePoint Web Services – Developed by Marc D Anderson (@sympmarc)

This is a jQuery library which abstracts SharePoint’s Web Services and makes them easier to use. It also includes functions which use the various Web Service operations to provide more useful (and cool) capabilities. It works entirely client side and requires no server install.

Written by jvossers

December 16, 2009 at 12:06 pm