a development blog for SharePoint,
Office 365, Azure, and whatever else falls across my path

Manually Like Links in SharePoint 2010

| No TrackBacks
Have you ever wanted SharePoint's social functionality to work a bit more like Facebook's? OOB, SharePoint allows you to like a page, but you have to be on that page to do it. Here is a JavaScript (+ jQuery) function to 'like' any URL that you want.

function likeUrl(pageUrl, pageTitle, context, successCallback, failCallback) {
    $.ajax({
        url: L_Menu_BaseUrl + "/_vti_bin/socialdatainternalservice.json/GetNormalizedPageUrl",
        dataType: 'json',
        type: 'POST',
        data: '{"pageUrl":"' + pageUrl + '"}',
        contentType: 'application/json; charset=utf-8',
        headers: { 'X-RequestDigest': document.getElementById('__REQUESTDIGEST').value },
        success: function (data) {
            $.ajax({
                url: L_Menu_BaseUrl + "/_vti_bin/socialdatainternalservice.json/AddQuickTag",
                dataType: 'json',
                type: 'POST',
                data: "{\"targetPage\":\"" + data.d + "\",\"title\":\"" + pageTitle.replace("\"", "\\\"") + "\",\"quickTagId\":0}",
                contentType: 'application/json; charset=utf-8',
                headers: { 'X-RequestDigest': document.getElementById('__REQUESTDIGEST').value },
                success: function () {
                    successCallback(context);
                },
                error: function () {
                    failCallback(context);
                }
            });
        },
        error: function () {
            failCallback(context);
        }
    });
}
Enhanced by Zemanta

Using a SPMetal Generated DataContext Across Site Collections

| 1 Comment | No TrackBacks
A while back I found myself in a situation where I needed to access list data across site collections. The site collection I needed to access was a sort-of central repository for data. For other uses in the project, I had run SPMetal to generate some strongly typed classes for all the lists. I thought I was in business. The plan was to use the DataContext object from another site collection, pointed to the "central repository" site collection, and just have at it.
 
using (SPSite site = new SPSite("http://centralrepository"))
{
  using (SPWeb web = site.OpenWeb())
  {
    using(CentralRepositoryDataContext dc = new CentralRepositoryDataContext(web))
    {
      //Access a list
      ListOneItem item = dc.ListOne.First();
    }
  }
}
So what did I find happen? I get an error saying that my list does not exist. I go and look in the "central repository" site collection, the list is there. I check the files that SPMetal generated and find no errors. So what's going on?

Well it was time to fire-up Reflector. Looking at the DataContext object, you find that communication with the lists takes place through the use of an object of type "SPServerDataConnection". This object is lazy-loaded (thankfully as this is our saving grace) and is passed the instance of the SPWeb object that you passed your DataContext object when you initialized it. In the constructor of this SPServerDataConnection object, that SPWeb object you passed in is being ignore and in its place SPContext.Current.SPWeb is being used. Unfortunately, later its being used to grab an instance of the list you're trying to access. So unless a list exists by the same name in the current web, you're going to blow up.

So, here comes the fun, and dangerous part. It's dangerous because we are using reflection to change an object that isn't our own. SP1 one may come in and fix all of this, or break this workaround. You just never know, so take this with a grain of salt. Since the SPServerDataConnection object is lazy-loaded in DataContext, we are going to initialize it ourselves and the way we want.

using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using Microsoft.SharePoint.Security;
using System.Reflection;
using Microsoft.SharePoint;
using System.Runtime.Serialization;

public partial class CentralRepositoryDataContext : Microsoft.SharePoint.Linq.DataContext
{  
  SPSite _Site = null;
  SPWeb _Web = null;

  partial void OnCreated()
  {
    AssemblyName asm = new AssemblyName("Microsoft.SharePoint.Linq, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");
    Assembly al = Assembly.Load(asm);
    Type dcType = al.GetType("Microsoft.SharePoint.Linq.Provider.SPServerDataConnection");
    object dcClass = FormatterServices.GetUninitializedObject(dcType);

    _Site = new SPSite(this.Web);
    _Web = _Site.OpenWeb(System.Web.HttpUtility.UrlDecode(new Uri(this.Web).PathAndQuery));

    dcType.InvokeMember("defaultSite",
      BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic,
      null, dcClass, new object[] { _Site });

    dcType.InvokeMember("defaultWeb",
      BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic,
      null, dcClass, new object[] { _Web });

    dcType.InvokeMember("defaultWebUrl",
      BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic,
      null, dcClass, new object[] { _Web.ServerRelativeUrl });

    Dictionary openedWebs = new Dictionary();
    openedWebs.Add(_Web.ServerRelativeUrl, _Web);

    dcType.InvokeMember("openedWebs",
      BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic,
      null, dcClass, new object[] { openedWebs });

    Type me = this.GetType().BaseType;
    me.InvokeMember("dataConnection",
      BindingFlags.SetField | BindingFlags.Instance | BindingFlags.NonPublic,
      null, this, new object[] { dcClass });
  }
}
Have fun!

Registering an External JavaScript File in a Sandboxed SharePoint Environment

| No TrackBacks
One of the easiest ways to load a JavaScript in SharePoint 2010 is to use a CustomAction with a Location='ScriptLink' and a ScriptSrc="*.js". However, this method comes with one stipulation, the ScriptSrc location must be one relative to the _layouts folder. This poses a problem when in a sandboxed environment as we cannot deploy to the server.

So, a way around this is to deploy your JavaScript file to a document library and register dynamically on the page. This can all be done with a single Elements.xml file.


<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">

  <ListInstance
    Id="{GUID}"
    FeatureId="00BFEA71-E717-4E80-AA17-D0C71B360101" 
    TemplateType="101" Title="DocLib" Url="DocLib" />

  <Module Name="DocLib" Url="DocLib">
    <File Path="DocLib\ext.js" Url="ext.js" />
  </Module>

  <CustomAction Id="{GUID}"
          Location="ScriptLink"
          ScriptBlock="var ds = document.createElement('script'); 
          ds.type='text/javascript'; 
          ds.src='~site/DocLib/ext.js'; 
          document.getElementsByTagName('head')[0].appendChild(ds);"></CustomAction>

</Elements>

You can also use this method to register CSS files. Although, you will find that it will registered before corev4.css. An alternative method would be to use the method above to register a version of jquery and another JavaScript file. Using the document.ready() method of jquery, dynamically add your CSS file there.
Enhanced by Zemanta

Custom Validation on a SharePoint PeopleEditor

| No TrackBacks
If you've ever needed to use a PeopleEditor control on a custom form then you've likely wanted to build in some custom logic to the validation process. While you cannot control who the user picks to validate (that would be a farm wide change), you can implement your own validation. An example class is below:


using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

public class CustomFilteredPeopleEditor : PeopleEditor
{
  public override void Validate()
  {
    base.Validate();

    if (this.IsValid && this.Entities.Count > 0)
    {
      //The user found someone and they resolved
      PickerEntity pe = this.Entities[0] as PickerEntity;
      SPUser selectedUser = SPContext.Current.Web.EnsureUser(pe.Key);

      //Do some extra validation and we
      //find we need to invalidate the user
      
      //You must set this for each entity
      pe.IsResolved = false;
      
      //Overall state of the people editor
      this.IsValid = false;

      if (!found)      
        this.ErrorMessage = "User does not pass special validation";      
    }
  }
}

Java Deployment Toolkit Script is Blanking My Page, Deferred Loading

| No TrackBacks
I was using a third party plugin recently that required the Java Deployment Toolkit Script (http://java.com/js/deployJava.js) to be loaded on the page. I loaded it up as I would any other script:

<script type="text/javascript" defer="defer" src="http://java.com/js/deployJava.js" />

(a caveat here is that I was in an .ascx)

When I went to my page, everything would load, but then the entire screen would go white. I check the source of the page, and it's entirely gone, with the exception of the object tag that the toolkit output. As far as I can tell, the toolkit writes its tags directly to the document object. Since loading was deferred, it just overwrote everything. It would be nice if the toolkit would accept some direction as to where we want the tags to be written.

Lesson learned: Do not defer load deployJava.js.

"Provider load failure", "Recycle IIS Application Pool" in VS2010 SharePoint Deployment

| 1 Comment | No TrackBacks
I came across this error again in Visual Studio 2010 when trying to deploy a SharePoint solution:

"Error occurred in deployment step 'Recycle IIS Application Pool': Provider load failure"

My previous solutions have been to restart my virt. I was knee deep in open windows this time and restarting sounded painful. So it was time to find an actual solution.

From searching online the consensus seemed to be that it was Windows Service based. I tried all the usual service restarts in powershell:
  • restart-service sptimerv4
  • restart-service spusercodev4
  • restart-service spadminv4
But none of those worked for me. Through a little more digging in the event logs, I came to the conclusion to try one more:
  • restart-service TrustedInstaller
Lo and behold I could deploy!
I hope someone can come back and validate this for me. No one seems to know how to reproduce this error so until it pops up again, I'll just have to assume this fixes it.
Enhanced by Zemanta

Report Viewer 2010 Rendering Issues in IE7

| No TrackBacks
When the reports were rendering in other browsers they were fine. In IE7, the reports either wouldn't render and freeze the browser, or would take 10 minutes or so to render and then would only show up if I re-sized the window or document... very odd behavior. After later analysis I found this behavior on only reports that contained graphs/charts.

After reading this article, Browser Support for ReportViewer Web Server Controls, and switching the DOCTYPES as it suggests, all was back to normal.

So this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
became:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

It's all about the quirks mode.
Enhanced by Zemanta