Meta: Attributes in Page Layouts Provisioned by Features are Unghosted (Customized)
18 Jan 2012 1:53 PM
Body:

One of my clients was having an issue where solution upgrade or redeployment didn't change certain page layouts. I checked the CustomizedPageStatus property of the affected layouts and it said they are customized. However, they had not actually been customized.

I activated the problem feature in another environment. Much to my surprise, the particular layouts indicated that they were customized immediately based on the provisioning for the module!

I didn't see any difference between the couple of layouts that exhibited this behavior and the others in the same module that were appropriately uncustomized post provisioning. Also, I observed that on a stock publishing portal site with no customizations the same is true of VariationRootPageLayout.aspx.

I figured I couldn't be the only one to have seen this behavior, so I asked the Internet and fellow MVP Rodrigo Pinto gave me the answer which I am blogging here in hopes that you'll have better luck finding the answer with the search engine of your choice than I did!

The affected page layouts all contained meta: attributes from SharePoint Designer as shown in the following example.

 

<%@ Page					
language="C#"   Inherits="Microsoft.SharePoint.Publishing…"
meta:progid="SharePoint.WebPartPage.Document" meta:webpartpageexpansion="full" %>

The presence of these causes SharePoint to 'unghost' or customize the page upon the initial provisioning. The affect is exactly the same as if the page was manually modified or uploaded into the library – subsequent upgrades do not affect the version in the site. I removed these from each of the affected layouts and checked in the changes. Once we upgraded the solution were able to run a script to re-ghost (uncustomize) the affected layouts. The script used Gary Lapointe's excellent extensions: http://blog.falchionconsulting.com/index.php/2007/09/re-ghosting-pages/

--Doug

Category: Publishing
Published: 1/18/2012 2:53 PM
Monkey Patching and SharePoint / Office 365
5 Jan 2012 8:03 PM
Body:

This post is a prelude for the one to follow in which I will show an application of this technique that overrides the workspace sizing routine in SharePoint 2010.

JavaScript is a wonderful language. It is easily accessible to anyone who understands C style syntax. This includes C# developers. However JavaScript is a dynamic language. In my experience most C# developers write poor JavaScript in a style that is appropriate to C#. However, getting the most out of SharePoint requires a good understanding of JavaScript and I am not talking about using jQuery or the Client Object Model. I am talking about fundamentals!

This is especially important if you are working in a sandbox environment like Office 365. It is very difficult (and often impossible) to fully achieve your UI goals given the restrictions of the sandbox architecture on the server side. However, with a good command of client side technologies, especially JavaScript, you can overcome many of these limitations.

The problem is that most of the really interesting components in SharePoint's JavaScript libraries are undocumented. Furthermore, the chances are good that if you make heavy use of what I am about to show you, you will be disappointed when you have to fix your work when the next version of SharePoint drops and these undocumented libraries change. So, consider that fair warning – this is a powerful technique, but one with serious risks over time!

As Wikipedia tells us: 'A monkey patch is a way to extend or modify the run-time code of dynamic languages without altering the original source code. This process has also been described as "duck punching".'

In JavaScript a function is a type and you can do things to functions that C# just doesn't allow. You can assign them to variables, pass them as arguments, and even change their values.

For example (see it here on jsFiddle):

function sayHi() {
alert('Hi!');
}


function doFunction(func) {
func();
}


doFunction(sayHi);


 

 

This may be one of the longest Hello World style examples you can write! In it the function sayHi is invoked via the doFunction function by passing sayHi as an argument.

Now, imagine that sayHi is actually part of one of SharePoint 2010's JavaScript libraries and you don't like how it works. In a farm deployment scenario you could (but never, ever should) just open the file in the SharePoint root and change it. This is not an option in sandbox mode because you have no access to the actual script file. This is where monkey patching comes in.

Consider this example (see it here on jsFiddle):

function sayHi() {
alert('Hi!');
}


function doFunction(func) {
func();
}


var oldSayHi = sayHi;
sayHi = function() {
alert('What is the opposite of bye?');
oldSayHi();
}


doFunction(sayHi);


 

In this version the code copies the pointer to the original sayHi function into a new variable and then changes the value of the variable sayHi to a new function that shows a different alert before calling the original version and you get two alerts. This, in a nutshell, is what monkey patching is all about. With it you can replace an existing function completely or do custom pre/post processing as shown in the example above.

To get a feel for what you might do with this technique, add the following script via a Content Editor Web Part to the home page of a stock team site and then add a new Shared Document by clicking the link.

if (typeof NewItem2 != 'undefined') {
    var oNI2 NewItem2;
    NewItem2 this.NewItem2;
}


NewItem2 function(eventurl) {
    alert('NewItem2 called');
    oNI2(eventurl);
};


 

You should see something similar to the following.

For those of you who inherit nightmares because of misuse of this technique; I'm sorry!

Happy coding!
--Doug

From: http://www.elumenotion.com/Blog/default.aspx

Category: Branding
Published: 1/5/2012 9:03 PM

I am a SharePoint Server MVP!
4 Dec 2011 1:58 PM
Body:

The last couple of months have been extremely busy for me; a demanding client, Cub Scout den leader activities, user group stuff, and life in general!

The best proof of this? I was awarded the MVP award for SharePoint Server while on a Cub Scout camping trip at the beginning of October and I am just now blogging about it. Not a good way to start… ;)

I am extremely grateful to be an MVP again! This is the second product for which I have been awarded; Access was the first waaay back in the late '90s. I am humbled to be in such excellent company!

Category: Random Whatnot
Published: 12/4/2011 2:58 PM

Using the People Picker Control in Sandbox Solutions / Office 365
23 Sep 2011 11:09 AM
Body:

The SharePoint sandbox does not allow you to use the Microsoft.SharePoint.WebControls namespace in your code. This means you can't instantiate objects from classes in that namespace or automate existing object instances of classes in that namespace. Because of this restriction, the MSDN documentation for PeopleEditor you will see the following:

Sandbox is a Server Thing

As I wrote above, this restriction is enforced by the sandbox. Available in Sandboxed Solutions: No means you can't use this class in your code. However, you are free to use any of the Microsoft.SharePoint.WebControls in your page markup. For example, if you create a custom master page it will almost certainly contain controls like CssRegistration and CssLink; both are members of Microsoft.SharePoint.WebControls. You can't write code that uses these classes. Therefore you can't call CssRegistration.Register() programmatically. You can use the control in markup assuming you can get the desired server side functionality declaratively via the control's property attributes. Markup as follows is permitted in pages deployed to the sandbox.

<SharePoint:CssRegistration name="<% $SPUrl:~sitecollection/Style Library/~language/Themable/Core Styles/controls.css %>" runat="server"/>


Pop Quiz!

If you want to use the PeopleEditor control in your sandbox solution can you use it in: a) an aspx page, b) a Web Part, or c) both?

The answer is a) an aspx page. However, to do anything with the selected value you will need a custom Web Part. The Web Part can't use the PeopleFinder class, but it can read all of the page's form fields. As it turns out, this is good enough with a little JavaScript glue to hold everything together on the client side.

Adding People Finder to a Page

First you must register the control namespace by including the following register directive (but it's probably on your page already).

<%@ register tagprefix="SharePoint"
namespace="Microsoft.SharePoint.WebControls"
assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>


 

Next include the control with the desired properties. In the example below the control allows the selection of a single user or SharePoint group. See the MSDN documentation for PeopleEditor for complete documentation.

<SharePoint:PeopleEditor
        AllowEmpty="false"
        ValidatorEnabled="false"
        id="assignedTo"
        runat="server"
        SelectionSet="User,SPGroup"
        MultiSelect="false"/>


On my custom task form the result looks like this:

Validating the Entry

Since automation of this control via the PeopleEditor class is forbidden, the page requires some JavaScript code to ensure that the entry is valid. It also must resolve the entry to make it easy for the page's Web Part to get the value from a form field. The picker's rendered state includes JavaScript that validates the user and the custom JavaScript takes advantage of the existing script. The only real trick is that the validation involves an asynchronous call to the server. The solution is to use a timer and observe the state of the picker's markup. As usual I use jQuery to observe and manipulate the markup.

There are four states the markup manifests:

  1. An entry that is confirmed as invalid

  2. An entry that is confirmed as valid

  3. No entry

  4. An entry that is not confirmed as either valid or invalid

To detect the first state the script looks for descendants of the control that contain the isresolved attribute set to false. Conversely, for the second state the script looks for descendants of the control that contain the isresolved attribute set to true.

Assuming the control is inside a content placeholder named PlaceHolderMain as is usual for SharePoint content pages, the HTML ID attribute of the control with a server ID of assignedTo is ctl00_PlaceHolderMain_assignedTo. The check for the first two states is as follows:

if ($('#ctl00_PlaceHolderMain_assignedTo').find('*[isresolved="False"]').length > 0) {
return false; //Confirmed invalid
}
if ($('#ctl00_PlaceHolderMain_assignedTo').find('*[isresolved="True"]').length > 0) {
return true; //Confirmed valid
}


 

The rendered control contains a div element that is empty if the user hasn't entered anything. To detect the third state, the script checks this div for an absence of children,

var entry = $('#ctl00_PlaceHolderMain_assignedTo_upLevelDiv').html();
if (entry === "" || entry === "&nbsp;") {
return true; //Valid - no entry
}

The final state is the one that requires the picker to validate itself before form submission. This process starts by calling the WebForm_DoCallback function. While it runs the script uses a timer to check for the first or second state. The completed validation functions are shown below.

var checkingName = false;

function checkName() {
if (!checkingName) {
checkingName = true;
var arg = getUplevel('ctl00_PlaceHolderMain_assignedTo');
var ctx = 'ctl00_PlaceHolderMain_assignedTo';
EntityEditorSetWaitCursor(ctx);
WebForm_DoCallback('ctl00$PlaceHolderMain$assignedTo', arg,
EntityEditorHandleCheckNameResult, ctx, EntityEditorHandleCheckNameError, true);

}
}

function nameIsValid() {
if ($('#ctl00_PlaceHolderMain_assignedTo').find('*[isresolved="False"]').length > 0) {
checkingName = false;
return false; //Confirmed invalid
}
if ($('#ctl00_PlaceHolderMain_assignedTo').find('*[isresolved="True"]').length > 0) {
checkingName = false;
return true; //Confirmed valid
}
var entry = $('#ctl00_PlaceHolderMain_assignedTo_upLevelDiv').html();
if (entry === "" || entry === "&nbsp;") {
return true; //Valid - no entry
}
//Not confirmed and not blank, ask the server
checkName();
//Wait half a second and check the markup to determine validity
setTimeout(ValidateAndSave, 500);
return false;
}


 

Getting the Result

If you submit the form after executing the above code, you can pick apart the form fields to read the entries. However, I prefer to use a bit more script to pull the valid name from the picker markup and stuff it into another field. This is cleaner and it decouples the Web Part code completely from the PeopleFinder. The following function returns the value from the picker in a valid, not empty state.

function getName() {
var data = $('#ctl00_PlaceHolderMain_assignedTo_upLevelDiv').html();
if (data !== "" && data !== "&nbsp;") {
var ret = $('#ctl00_PlaceHolderMain_assignedTo').find('div#divEntityData').attr('key');
if (ret !== undefined) {
return ret;
}
else {
return "";
}
}
return "";
}


In the Web Part I use the value directly without additional parsing.

SPUser user = web.EnsureUser(task.assignedTo);
SPFieldUserValue at = new SPFieldUserValue(web, user.ID, user.LoginName);

 

Happy coding!
--Doug

Category: Sandbox/Office 365
Published: 9/23/2011 12:09 PM

jQuery Plugin to Display SharePoint 2010 List Views
30 Aug 2011 8:39 AM
Body:

I'm working on a new line of products for SharePoint Online / Office 365. A few of the pages need to display list views whose locations are unknown at design time – the lists are created on the fly and can have any title and a single page displays a view based on what the user chooses. This means that I can't use a data view Web Part because it is tied to a specific list and view.

RenderAsHtml

My first attempt involved the use of the RenderAsHtml method of SPList and SPView. The code for this is super simple:

When I tested the Web Part, I couldn't believe my eyes. It worked on the very first try!

Unfortunately, this was just my old friend SharePoint playing a fun joke on me. As soon as I clicked an item I saw the following error message:

Yes - This item is no longer available. It may have been deleted by another user.

The reason this error displays is that the HTML emitted by RenderAsHtml is for the V3 UI. It doesn't match what the SharePoint 2010 JavaScript expects and the result is this error. I started digging into how I might make this sucker work. Along the way I learned a lot about the client side behavior. In fact, I learned so much that I decided a general solution based on RenderAsHtml was not going to fly.

Near as I can tell, the RenderAsHtml method in SharePoint 2010 is Microsoft's little joke on us – it's there to make you think you can get it to work if you just try hard enough.

jQuery Pluggin

My next step was to cry out in frustration and rage redouble my efforts and get creative. It occurred to me that a SharePoint site is just full of properly functioning list views. I thought 'hmmm, jQuery Get()'. In jQuery the Get() method issues an asynchronous HTTP get for a page and gives you a string that contains the returned HTML. The final solution is a jQuery plugin extracts the view HTML and script from a list's view page, e.g. AllItems.aspx, and inserts it into the current page.

Of course it's not quite that simple – I'll explain how it all works in the next post. In the meantime, you can get the plugin from here: http://www.elumenotion.com/Downloads/listViewPlugin.zip

To use the plugin you will need to first include jQuery – I built this with jQuery 1.6.2 and I have not tested it with earlier versions!

Second: you will need to modify the site's default master page to include a DIV that marks the end of PlaceHolderMain:

<div id="MSO_ContentDivEnd" style='display:none'></div> makes it easy for the pluggin to know where the view ends. You can probably modify the plugin to avoid this requirement, but I have a custom master page already and this was the easiest solution for me!

Third: Include the listViewPlugin.js. (very important!)

Fourth: Add an anchor element to the page where you want the view as follows:

<a href=[http://www.elumenotion.com/listurl/viewPage.aspx] class="listView"></a>


 

The href is the URL to the view and class is either listView or libraryView depending on if the view is a list or a library, respectively.

Finally: add the script to load the view on document ready.

<script type="text/JavaScript">


jQuery(document).ready(function($) {


$('a.libraryView').loadListView();


});


</script>


 

The Result

Below you see the result in a Content Editor Web Part. Note that the menu works!

Filtering, sorting, and paging all work too!

Let's filter by document type.

I've tested this on IE, FireFox, Chrome, and Safari. It works with basic list and library types. It does not work with Calendar or GANTT views – however, in the case of Calendar views I think the missing step is to link to the appropriate style sheet.

Enjoy!
--Doug

Category: Random Whatnot
Published: 8/30/2011 9:39 AM

Tip: Debugging Feature Event Receivers in SharePoint Sandbox Solutions
5 Aug 2011 8:39 AM
Body:

I accidently discovered this yesterday and thought I'd share. If only this same technique worked with farm solutions…

If you've done any sandbox development you have probably had many moments when you pressed F5 to start debugging or done Build|Deploy only to see a failure because one of your feature receivers threw an error. To debug these problems in farm mode, you attach the debugger and then try to activate the feature via the site, PowerShell, or stsadm. It's kind of a pain.

In sandbox mode, if you attach the debugger to the worker process manually before you deploy the solution you can catch and debug problems in your feature event receivers. To do so, select Debug|Attach to process and attach to SPUCWorkerProcess.exe.

Then, simply hit F5 or select Build|Deploy Solution. I wish I'd figured this out a year and a half ago. It would have saved me a great deal of time!

Happy coding!
--Doug

Category: SharePoint Developer Productivity Tip
Published: 8/5/2011 9:39 AM

Hiding a SharePoint Ribbon Button with CSS
2 Aug 2011 11:17 AM
Body:

I spent far too long this morning trying to hide a single ribbon button on a single page. The page is part of a sandbox solution so I couldn't do the work server side, and because the page was a one-off in a library I didn't want to use a HideCustomAction.

My first attempt was CSS, but that didn't work. I assumed (incorrectly) that the reason my style didn't stick was because the ribbon's JavaScript was toggling the display. The problem turned out to be that I didn't understand CSS quite as well as I thought!

The ribbon's various elements use ID attribute values that contain periods. The values correspond to the cmdui.xml file that defines the ribbons for SharePoint Foundation. To affect the elements by via their ID's with CSS you have to use an escape \ (backslash) before the period. For example, to hide the New Item button, use the following syntax:

#Ribbon\.ListItem\.New\.NewListItem-Large
{
display:none;
}

 

This is why I love software development! Frustration turns to joy!

--Doug

Category: Random Whatnot
Published: 8/2/2011 12:17 PM
Office 365/SharePoint Sandbox Service Pages in Action – Autocomplete Text Boxes
26 Jul 2011 6:38 AM
Body:

A couple weeks ago I wrote about an Unorthodox Architecture for Services in SharePoint Sandbox Solutions and Office 365. This post is about an application of this architecture to create an autocomplete text box using jQuery UI. An autocomplete field looks like this:

It is a great alternative to the stock lookup field UI when the source list of the lookup field has a large amount of data. With hundreds of items the standard dropdown list just isn't a good experience. The jQuery Autocomplete widget makes it easy to build a field that displays a list as the user types. In this example the JavaScript is simple and the underlying field is "single line of text". If you need to ensure the entry matches a value from the lookup list you'll need to do some extra work. That said, here is the JavaScript:

$("#client").autocomplete({
source: function(request, response) {
url = L_Menu_BaseUrl + "/ServicePages/AutoComplete.aspx?l=Clients&f=Title&v=" + request.term;
$.getJSON(url, function(data) {
response(data);
});
},
minLength: 1
});


 

The url in the above script is to a service page that contains a Web Part whose code is shown at the bottom of this post. The variable L_Menu_BaseUrl is declared and populated by SharePoint's own JavaScript libraries and it contains the Web's server relative URL. The Web Part reads the query string and runs a CAML query against the specified list to search for values that begin with the specified term. The result is written to the page as JSON by using the JavaScriptSerializer to serialize a List<string> of the specified field for each item in the result set.

Enjoy!
--Doug

//Inputs are
//l=List
//f=Field
//v=Value
//max=RowLimit
[ToolboxItemAttribute(false)]
public class AutoComplete : WebPart
{
protected override void Render(HtmlTextWriter writer)
{
var items = GetItems();
if (items.Count > 0)
{
JavaScriptSerializer js = new JavaScriptSerializer();
writer.Write(js.Serialize(items));
}
}

private List<string> GetItems()
{
var items = new List<string>();
string listName = Page.Request.Params["l"];
string field = Page.Request.Params["f"];
string val = Page.Request.Params["v"];
uint max = 10;

//Parse the query string parameters and return an empty list if there is a problem
if (Page.Request.Params["max"] != null)
{
try { max = uint.Parse(Page.Request.Params["max"]); }
catch { } //ignore the error and return 10 items
}

if (string.IsNullOrEmpty(listName) || string.IsNullOrEmpty(field) || string.IsNullOrEmpty(val))
{
return items;
}

//Build and execute the query
string queryXML = string.Format(@
"<OrderBy><FieldRef Name='{0}' /></OrderBy><Where><BeginsWith><FieldRef Name='{0}' />
<Value Type='Text'>{1}</Value></BeginsWith></Where>", field, val);

string viewFields = string.Format(@"<FieldRef Name='{0}'/>", field);

SPList list = SPContext.Current.Web.Lists.TryGetList(listName);

if (list == null) return items;

SPQuery query = new SPQuery();
query.Query = queryXML;
query.ViewFields = viewFields;
query.RowLimit = max;
var listItems = list.GetItems(query);

//Put the returned items into the list
foreach (SPListItem listItem in listItems)
{
items.Add(listItem[field].ToString());
}
return items;
}
}


Category: Random Whatnot
Published: 7/26/2011 7:38 AM

How Office 365 Made Me Hate My Windows 7 Phone
17 Jul 2011 12:44 PM
Body:

I had a hard time deciding on a title for this post. The experience I am going through is just ridiculous on so many levels and there is a lot of hate to go around.

SharePoint Online in Office 365 uses claims based authentication which is cool, and it supports federated authentication and trust to let you use your corporate credentials which is even cooler. Microsoft has been a leader in this space for a long time and everyone I know has a Live ID. However, for reasons that are unexplained, Office 365 doesn't use Live ID. Instead it has its own federated system that gives users super long IDs like SomeOne@SomeCompany.onmicrosoft.com.

Strangely enough SharePoint Online does support Live ID via the (also very cool) external user access features. Basically, you invite an external user and they can access your site using LiveID as long as the Live ID is a Hotmail.com email address.

Yes! A platform that includes Exchange requires the use of hotmail.com for LiveID in spite of the fact that Live ID itself has no such requirement.

A couple weeks ago I tweeted this depressing but funny comic of various org charts including a spot-on depiction of Microsoft's. I suspect that this chart explains Microsoft's reasoning behind this idiotic requirement. Someone's bonus was threatened by Exchange in 365, but by requiring Hotmail.com they can keep the numbers up. (I freely admit and emphasize that this is pure speculation with no basis in fact).

I wanted to try this External User feature out, so (trusting fool am I) I changed my existing LiveID which I have been using happily for years to use a Hotmail.com address.

Right away there were problems.

First, I noticed that Live Messenger now blocks most (but not all) of my contacts. That's a really big problem that I have yet to solve.

Xbox Live worked like a champ, as did Zune. Both picked up the change and both were painless.

My Windows Phone 7? Well… You can't change the Live ID on a Windows Phone once it is set up without resetting it to the factory defaults. Pure genius! I wonder if changing this is one of the 500 new features in Mango.

The moral of this story? Never change your Live ID because Microsoft does not have its act together and you will suffer greatly for it if you do.

Category: Random Whatnot
Published: 7/17/2011 1:44 PM
Unorthodox Architecture for Services in SharePoint Sandbox Solutions and Office 365
15 Jul 2011 12:12 PM
Body:

These days we are spending a lot of time on a series of sandbox applications that will run on Office 365. If you are a follower of this blog you know I was more than a bit disappointed in the restrictions the SharePoint 2010 sandbox implementation imposes. However, if you want to play in the sandbox, you can either just say no to requirements that aren't directly supported, or you can earn your money and get creative!

I've been getting creative. These applications are going to be great - that said, some elements of the architecture are unorthodox and even a little strange. This post details one of the elements.

Goals

Expose services to external clients that:

  1. Hide their underlying technology from the caller and hide the application's underlying implementation from prying eyes
  2. Support all current web development stacks
  3. Perform well
  4. Are easy to create and consume

Client side technologies like the Client Object Model (JSOM) and SPServices are awesome. However, they fail goal #1. If page's business logic is implemented in JavaScript it is available to any and all who care to look. Worse, they both encourage tight coupling between the client UI and SharePoint. Both support JavaScript, and the Client Object Model also supports managed (.NET) clients and Silverlight. However, both have minor learning curves for developers who mainly write C# and use the server object model.

What we really want is for the client to consume services via an abstraction that hides the details and helps minimize dependencies. Unfortunately, you can't deploy traditional asmx or WCF services to the SharePoint Sandbox.

Before we get into the solution, please be aware that I am not saying that this architecture is better than JSOM, SPServices, or the REST API – I'm just saying it is a better fit for our particular needs for certain specific applications. It also illustrates an interesting technique that you can add to your own arsenal.

Solution

While it is not possible to deploy traditional Web services, we can come close. A Web service is just an http endpoint to which we can pass arguments and from which we can read a response. Conceptually, the only differences between an aspx page and a Web service are the calling convention and (usually) the content type (e.g. 'text/html' versus 'text/xml' or 'application/json'). You can create an aspx page that works just like an asmx or WCF service if you want – you just have to handle the plumbing yourself.

Our applications have lots of JavaScript and JSON is the easiest format to consume. It is easy to create a page in the sandbox that returns JSON based on query string or form POST parameters. Here's how!

serviceContainer.master Master Page

This is a very simple master page with a single ContentPlaceHolder that the content page overrides with the service web part.

servicePage.aspx

Service pages are stored in a folder or library off the root of the web allowing a relative URL reference to serviceContainer.master. Each page uses SPUserCodeWebPart to load the a sandboxed Web Part that contains the actual implementation of the service.

A welcome byproduct of using a page in a library is the ability to use the standard SharePoint security model to control access to the page (and hence the service). This is a clear advantage for this technique compared to both the Client Object Model and SPServices.

service.webPart

The service Web part reads any required arguments from the query string and/or post parameters from the Page.Request and does its work returning the format of your choice. In the screen shot below it is returning a JSON string that contains the current Web's Title. Because the part is stateless all of the work is done in Render().

The result looks like this:

It could just as easily be XML or any other type of content. The master page and the page that contain the Web Part are empty. What comes out of the Render method is the body of the response.

Note: Unfortunately because the Web Part lacks access to the outer page you can't set the response content type properly. Having tested with IE, FireFox, Chrome, Safari, and Opera I can confirm that script can still consume the result without issues using jQuery.getJSON(). I have also emitted dynamically JavaScript and used the page as the src attribute in <script> elements. The response is always of type text/html. Be aware that this is a risk and may cause issues with some clients!

Performance

Testing shows that this solution provides performance that is comparable (and at times significantly better) than the equivalent Client Object Model code. The tests used a site on SharePoint Online (Office 365).

ServicePage

The service page was invoked via a .NET console application using the HttpWebRequest class. The code below shows the function that makes the call.

Fiddler shows that this call sent 1258 bytes and received 866 bytes in approximately 150 milliseconds.

Client Object Model

Two passes were tested with the JSOM from the same console application. The first initialized the ClientContext each time and the second reused an existing ClientContext. It turns out that instantiating the client context calls the sites.asmx service. Therefore each call resulted in two round trips. The important lesson learned here is that you should hold on to the context and not recreate it for the best performance. The code for the second version of the test method is shown below. To make the comparison fair, the code explicitly asks for only the Title property of the current Web.

Fiddler shows that this call sent 2022 bytes and received 724 bytes in approximately 225 milliseconds.

The results for one set of tests are as follows:

Round 1

10 calls to the sandbox page took 23544921 ticks

10 calls with the client object model took 39882812 ticks

10 calls with the client object model with one context took 22656250 ticks

 

Round 2

10 calls to the sandbox page took 21337890 ticks

10 calls with the client object model took 40322266 ticks

10 calls with the client object model with one context took 30048828 ticks

 

Round 3

10 calls to the sandbox page took 17460938 ticks

10 calls with the client object model took 43066406 ticks

10 calls with the client object model with one context took 22109375 ticks

 

Round 4 (With Fiddler On)

10 calls to the sandbox page took 15449219 ticks

10 calls with the client object model took 38222656 ticks

10 calls with the client object model with one context took 24550782 ticks

 

Round 5

100 calls to the sandbox page took 171845703 ticks

100 calls with the client object model took 473261719 ticks

100 calls with the client object model with one context took 240097656 ticks

Conclusion

In the first round the JSOM performed slightly better than the service page. In subsequent rounds the service page's performance was significantly better. I speculate that the differences are due to unknown factors on my test Office 365 tenancy. Therefore, the only conclusion I am willing to stand by is that using this technique is at worst performance neutral but that it might be better.

The tests used a trivial scenario. In spite of the excellent batching capabilities found in the JSOM, real world scenarios often require multiple roundtrips where each operation depends on data from a previous operation that is not on the client. This architecture potentially offers much better performance by combining such operations into a single round trip.

Category: Random Whatnot
Published: 7/15/2011 1:12 PM

Screen Scraped Authentication to Office 365 and SharePoint Online
1 Jul 2011 3:25 PM
Body:

Way back in April, Microsoft published a very good article by Robert Bogue entitled Remote Authentication in SharePoint Online Using Claims-Based Authentication. This article describes one way to get the FedAuth cookie required to talk to SharePoint online via any of its many interfaces. While the article is very informative and the associated code works, I didn't like the implementation as it involved the web browser control and PInvoke (The former has a lot of overhead, the latter requires full trust).

So, I decided to try to do the same thing using tried and true screen-scraping techniques. I got it working and shortly thereafter I discovered this post and sample by Wictor WilénHow to do active authentication to Office 365 and SharePoint Online. Wiktor's approach is more robust than my screen-scraping solution and it uses Windows Identity Foundation directly to use the active federation service provided by the STS is located at https://login.microsoftonline.com/extSTS.srf. Wiktor's approach is also more efficient and requires fewer round-trips to the server to do the job.

He also posted it a few days before I got mine working!

If Wiktor's solution is so much better than mine, why am I posting this?

The reason is that Wiktor's solution depends on Windows Identity Foundation and mine will work on any platform capable of dealing with HTTP and cookies – including Windows XP and various non-Windows development stacks. Although the sample is written in C# you should have no trouble converting it to the language of your choice. Hopefully, the sample will be helpful to a few folks. J

You can download the sample from here. It consists of a Visual Studio 2010 C# solution containing a console application and a class library. If you do not have Visual Studio (Express will work) and you are looking at this sample because you are using something other than .NET, the file you want to look at is O365AuthenticationScraper.cs. It is heavily commented and should be easy to follow.

--Doug

Category: Security
Published: 7/1/2011 4:25 PM
SharePoint Security and the Object Model – Part 3
1 Jul 2011 11:52 AM
Body:

Authorization and Access Control – Evaluating Permissions

This is the third part of a three part post on security object model basics in SharePoint. The other posts are: SharePoint Security and the Object Model – Part 1 and SharePoint Security and the Object Model – Part 2. The first post covers users and groups, the second post covers the mechanics of defining and applying permissions. This post is about using and applying user, groups, and permissions to control the user's access and experience in a site in the UI and in code.

Why is this Necessary?

Before I talk about using permissions to control access (which is very easy), let's consider why you need to know anything beyond the first two posts in this series. In the first post, you learned how to work with users and groups. That post even covered how to add users to groups based on claims with SPClaim and SPClaimProviderManager. If you know how users and groups work, why not write code that checks permission based on user or group names?

I'd bet that most of you instinctively know why this is a very bad idea – users and groups change. Obviously you wouldn't want to write your security code based on them. But, what about claims? If you are using claims you will be tempted to write code that checks for a claim via ClaimsIdentity.Claims or a custom claim manager. If you've seen my Claims Identity presentation, you may recall that I use an example where the doorman lets Mary enter because she has a claim that says she is over 21. Writing code that checks for the claim directly is just as big a mistake as checking for a specific user or group, but for slightly different reasons.

Checking for a claim directly creates a dependency between your code and Windows Identity Foundation. Depending on how you do it, it might also create a dependency on a specific identity provider. It also makes your code very inflexible and requires development if the rules change.

You should never perform authorization checks based on users, groups, or claims. Instead you should use permission levels and role assignments as discussed in part 2 of this series. To perform authorization checks you should always use the simple techniques discussed below for maximum flexibility. Although doing all the work discussed in parts one and two to enable these techniques may seem like a lot of work, it is the least amount of work that doesn't create lots of trouble over time.

DoesUserHavePermissions

ISecurableObject defines a method named DoesUserHavePermissions. The abstract SPSecurableObject provides an implementation of this method that is overridden in the various securable object implementations, i.e. SPWeb, SPList, and SPListItem. The base definition of DoeasUserHavePermissions is simple (see part 2 for more on SPBasePermissions):

public virtual bool DoesUserHavePermissions(SPBasePermissions permissionMask)

   

It returns true or false given a permission mask for a given web, list, or item for the current user. For example:

bool hasPermission = SPContext.Current.Item.DoesUserHavePermissions(SPBasePermissions.EditListItems);
writer.Write("User can edit this item = " + hasPermission.ToString());

   

Note: In older versions of SharePoint this method has the bizarre behavior of throwing UnauthorizedException instead of returning false. This alternative code produces the same result:

permissionMask == SPBasePermissions.EmptyMask || (itemToCheck.EffectiveBasePermissions & permissionMask) == permissionMask;

(itemToCheck is a valid instance of an SPSecurableObject)

SPSecurityTrimmedControl

SPSecurityTrimmedControl is a handy web control that provides a container for other controls and markup. You simply provide a permission mask for the PermissionString property and SharePoint takes care of the rest! You can use it on pages and in user controls and web parts. Unfortunately, this control is located in the Microsoft.SharePoint.WebControls namespace and is therefore unavailable to sandbox code. Code is the operative word – you can still use it on pages as long as it is in the page markup and not in a control. You can specify multiple permissions by separating the values with commas.

The documentation for this control is good and there are plenty of posts out there the describe its full functionality. The SDK article is here.

CustomAction Rights

The CustomAction feature element allows you to add items to most menus and also to the ribbon. It exposes a Rights attribute that specifies a set of rights that the user must have for the link to be visible, for example, "ViewListItems,ManageAlerts". Simillar functionality is exposed by the SPUserCustomAction class. It has a Rights property that accepts a permissions mask.

Category: Security
Published: 7/1/2011 12:52 PM
Serialization in Sandbox Solutions
3 Jun 2011 10:06 AM
Body:

If you have the pleasure of writing sandbox solutions, perhaps for Office365, you know that doing so involves a wide variety of creative workarounds. Serialization is one area where this creativity comes into play.

The problem with the XMLSerializer is that it creates and loads a serialization assembly on the fly. This doesn't work in the sandbox. A good discussion and one solution to this problem is discussed here: Using the Application Setting Manager in Sandboxed Solutions. An alternative approach is discussed here: Serialization Inside SandBox Solutions.

My alternative is to avoid XML Serialization altogether and use JSON via the JavaScriptSerializer class. This lovely class does not require an assembly and it works. Best of all, it creates a format that is easy to use in JavaScript which I tend to use heavily in SandBox solutions.

A terrific overview of this class can be found here on Rakki Muthukumar's blog: ASP.NET – JSON – Serialization and Deserialization.

 

Category: Random Whatnot
Published: 6/3/2011 11:06 AM
Creating List Indexes with the SharePoint Object Model
2 Jun 2011 9:18 AM
Body:

Many scenarios in SharePoint 2010 require you to create indexes on lists – e.g. enforcing unique values, CAML joins, and referential integrity. I was writing some code yesterday that needed an index and I wasn't able to find a sample – so I figured I'd put up a quick post.

Before you get started, understand that you can only index certain field types and that compound indexes are even more restricted. See this page for the types of indexable fields: Metadata Navigation and Filtering.

Here is the sample c# code. (list is an instance of SPList for the list that will be indexed)

Single Column Index

SPField title = list.Fields["Title"];
title.Indexed = true;
title.Update();

list.FieldIndexes.Add(title);

Compound Index

SPField createdBy = list.Fields["Created By"];
createdBy.Indexed = true;
createdBy.Update();

SPField created = list.Fields["Created"];
created.Indexed = true;
created.Update();

list.FieldIndexes.Add(createdBy, created);

Category: Lists
Published: 6/2/2011 10:18 AM
by Elumenotion Blog: Posts
Filed under:
Atlanta Code Camp 2011–June 25
1 Jun 2011 9:32 AM
Body:

At long last, we're happy to announce the 2011 edition of the Atlanta Code Camp which will be held at the Southern Polytechnic State University.

Event Details

Call for Speakers:

click here

Registration:

click here

Date:

Saturday, June 25th

Registration Time:

7:45AM to 8:30AM

Time:

8:30AM to 5:30PM

Location:

Southern Polytechnic State University in Marietta, GA

Address:

1100 South Marietta Parkway, Marietta, GA 30060 (directions)

Cost:

This event is free to everyone that wishes to attend, as long as capacity is not exceeded in the event facilities.

Breakfast and Lunch:

Free of charge

   

If you're not familiar with code camps, they are free community‐focused events by and for the .NET developer community. The Atlanta Code Camp draws upon the expertise of local and regional developers, architects, and experts who come together to share their real world experiences, lessons learned, best practices, and general knowledge with other interested individuals.

This event is unique in that it is "for the community, by the community" and is free for all that desire to attend. In past years, the Atlanta Code Camp has provided free training and networking opportunities for 300 of the best, most motivated development professionals. With our larger facility, we're expecting this year to be even bigger and better.

Published: 6/1/2011 10:32 AM
More Posts Next page »