SP2010 Workflows – Set permissions on list item using people picker multi-select

August 13, 2014 Leave a comment

It goes without saying that I would use the SP2013 workflows if you can but sometimes we work in environments which do not have these setup.

Stuck with SP2010 workflows in a SP2013 farm, this one had me stumped because if I used the Replace List Item Permissions action and used a multi-select people picker field it would not set the permissions for these users.

The solution:

First use the Replace List Item Permissions and set any permissions you require, ie Full control for owners group and so on.

Then use the Add List Item Permissions and use the multi-select people picker field you require and return field As String.

Easy once you solve the puzzle!

SP2013 REST API – Find if user is member of SharePoint group

August 13, 2014 Leave a comment

Use this to find if user is a member of a SP group using client side code.

jQuery Code Snippet:

$(function(){
isMember(“Some Group Name”);
});

function isMember(groupName){
$.ajax({
url: _spPageContextInfo.webAbsoluteUrl + “/_api/web/sitegroups/getByName(‘”+ groupName +”‘)/Users?$filter=Id eq ” + _spPageContextInfo.userId,
method: “GET”,
headers: { “Accept”: “application/json; odata=verbose” },
success: function(data){
if(data.d.results[0] != undefined){
//User is a Member, do something or return true
}
}
});
}

Note: For the group you are testing make sure to change the group settings to – “Who can view the membership of the group?” = Everyone
Otherwise users with low permissions will get a login prompt.

PowerShell Remoting – Creating a Site Collection with a new Content Database

October 19, 2012 Leave a comment

So I had my PowerShell script which creates a managed path, content database and then a Site Collection with sub-sites all working nicely when running locally.
But my client had a custom PowerShell script to move the applications through the various environments and remotley execute scripts, so when my script was invoked remotely the following line failed:

New-SPSite -name $($siteCollection.Name) -url $url -contentDatabase $contentDatabase -ownerAlias $ownerAlias -secondaryOwnerAlias $secondaryOwnerAlias -template $($siteCollection.SiteTemplate)
Error: User not found

Problem
So following the steps to ensure remoting is setup correctly using the steps in the link at the end of this article. And ensuring that when I used the Invoke-Command that I used CredSSP and passed through the credentials I still faced a problem.

The problem is caused when the script creates the content database using New-SPContentDatabase, the user executing the script becomes the database owner.
So when you run Add-SPShellAdmin against your new content database using the user executing the script you will receive the following error:
“Cannot add <user> to the SharePoint_Shell_Access role of the database <databaseName>. A possible cause of this error is that the account name was already added to the database as a login using a different username.”

Solution
First we must change the database owner after the script creates our new content database:

New-SPContentDatabase -name $newContentDatabase -webApplication $webApplicationUrl -maxsitecount 1 -warningsitecount 0

Add-PSSnapin SqlServerCmdletSnapin100
Add-PSSnapin SqlServerProviderSnapin100
invoke-sqlcmd -inputFile $pathToSQLScript -ServerInstance $serverInstance -Database $newContentDatabase -Variable UserName=domain\newDBOwner

The SQL script:
BEGIN TRY exec sp_dropuser N’$(UserName)’ END TRY
BEGIN CATCH END CATCH
go
sp_changedbowner @loginame=N’$(UserName)’

Then back in PowerShell we can now run the Add-SPShellAdmin command:

Add-SPShellAdmin -UserName $(whoami) -database $newContentDatabase

And finally we can create our Site Collection remotely!

The following post help me get to this resolution:
http://social.technet.microsoft.com/Forums/en-US/sharepoint2010setup/thread/09b60466-5432-48c9-aedd-1af343e957de/

Adding an image uploader to the Publishing Image site column

January 6, 2012 12 comments

Click here to download the WSP

Click here to download the Source

Mission
To add a much needed feature to the Publishing Image site column, which is the ability to upload an image from your computer when adding a page or roll up image on a publishing page.
The current SharePoint functionality is that you must upload your image to the image library first, then you can edit your page and select the uploaded image, not very user friendly.

Solution
Use jQuery to add an upload button that then opens a SharePoint dialog in which you can upload an image to the image library. Once uploaded successfully you click continue and insert your new image.

Here is my solution structure

So first lets look at the SiteScripts module that adds our JavaScript reference and initializes it.

<?xml version="1.0" encoding="utf-8"?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<CustomAction
Location="ScriptLink"
ScriptBlock="
function loadImageUploaderScript(src) {
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.type = 'text/javascript';
var url = window.location.toString();
url = typeof(L_Menu_BaseUrl) != 'undefined' ? L_Menu_BaseUrl :
url.substr(0, url.indexOf('/_layouts'));
script.src = url + src;
head.appendChild(script);
}
_spBodyOnLoadFunctionNames.push(
'loadImageUploaderScript(\'/Style Library/js/publishingImageUploader.js\')');
" Sequence="120">
</CustomAction>
</Elements>

Now lets look at the publishingImageUploader.js
The script checks that we are on the AssetImagePicker window first, it then creates a input button, shortens the “Select Image” textbox and appends our button. It then attaches a click event.

$(function () {
if (window.location.href.indexOf("AssetImagePicker.aspx") > 0) {
var uploader = $("<input id='uploader' type='button' value='Upload image' />");
$(".ms-assetimagedialog-longtextbox").width("300px");
$("[id$=_assetSelectedImage_PickerLaunchButton]").parent().append(uploader);

uploader.click(function () {
publishingImageUploader();
});
}
});

function publishingImageUploader() {
var dialogOptions = SP.UI.$create_DialogOptions();
var url = window.location.href;
dialogOptions.url = url.substr(0, url.lastIndexOf("/_layouts/")+10) +
"PublishingImageUploader.aspx";
dialogOptions.width = 750;
dialogOptions.height = 200;
dialogOptions.dialogReturnValueCallback = Function.createDelegate(null,
CloseCallback);
SP.UI.ModalDialog.showModalDialog(dialogOptions);
return false;
}

function CloseCallback(strReturnValue, url) {
$("[id$=_assetSelectedImage_AssetUrlInput]").val(url);
}

The click event opens our PublishingImageUploader.aspx shown below, which is in the layouts folder and opened in a SharePoint dialog.

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI"
Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" %>
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="PublishingImageUploader.aspx.cs"
Inherits="PublishingImageUploader.Layouts.PublishingImageUploader"
DynamicMasterPageFile="~masterurl/default.master" %>

<asp:Content ID="PageHead" ContentPlaceHolderID="PlaceHolderAdditionalPageHead"
runat="server">
<script type="text/javascript">
function CloseUploader() {
SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK,
'<%=ImageUrl %>');
}
</script>
</asp:Content>

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
<h3>Upload image</h3>
<input type="file" id="ImageFile" runat="server" />
<input type="button" id="Upload" value="Upload" onserverclick="UploadImage"
runat="server" />
<asp:panel id="ClosePanel" visible="false"  runat="server">
<asp:Literal id="Message" runat="server" />
<input type="button" id="close-button" value="Continue"
onclick="CloseUploader()" />
</asp:panel>
</asp:Content>

<asp:Content ID="PageTitle" ContentPlaceHolderID="PlaceHolderPageTitle"
runat="server">
Upload image
</asp:Content>

<asp:Content ID="PageTitleInTitleArea"
ContentPlaceHolderID="PlaceHolderPageTitleInTitleArea" runat="server" >
Upload image
</asp:Content>

Now our codebehind looks like this

using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Publishing;

namespace PublishingImageUploader.Layouts
{
public partial class PublishingImageUploader : LayoutsPageBase
{
public string ImageUrl { get; set; }

protected void UploadImage(Object sender, EventArgs args)
{
var pubWeb = PublishingWeb.GetPublishingWeb(SPContext.Current.Web);
var folder = pubWeb.ImagesLibrary.RootFolder;
var file = folder.Files.Add(string.Format("{0}/{1}", folder.Url,
ImageFile.PostedFile.FileName), ImageFile.PostedFile.InputStream, true);
ImageUrl = file.ServerRelativeUrl;
Message.Text = string.Format("Image {0} was uploaded successfully!", file.Name);
ClosePanel.Visible = true;
}
}
}

So the above uploads our image to SharePoint on click and then shows a success message and a continue button, it also sets the ImageUrl property which is used in our dialog callback function so that the uploaded image url is added to the “Select image” textbox on close.

Note: dependant on the global jQuery WSP, get it here: https://simonovens.wordpress.com/2011/11/14/sandbox-sharepoint-online-jquery-jquery-ui-wsp-package/

See it in action

Click here to download the WSP

Click here to download the Source

Bulk create custom publishing pages with various site column types

November 27, 2011 Leave a comment

I was creating an A to Z  filtering solution that utilised the XSLTListViewWebPart embedded into a custom PageLayout and I needed a lot of publishing pages to be created for a UI test. I am sure the tester will be happy to use this script also as I have seen them do this manually before.
I thought surely someone has done all the hardwork already and sure enough Brendan Newell had most of  script I needed. His nice solution takes in an XML file and creates basic publishing pages. 

What I needed is to extend this so I could create a custom publishing page that uses my custom PageLayout and ContentType.
Some of the fields had unique field types so I have written this up for my future reference and hope others find it useful to extend on.
(Note: I will add more field types as I come across them)

Open up SharePoint 2010 Management Shell and navigate to the saved PowerShell file we will create below.
Invoke the PowerShell script passing the path to your XML and SharePoint site.

PS c:\scripts> .\page-layout-creator.ps1 “c:\pages.xml” http://sitecollection/sites/somesite

page-layout-creator.ps1

[xml]$pagesXML = Get-Content $args[0]
$siteUrl = $args[1]

$site = New-Object Microsoft.SharePoint.SPSite($siteUrl)
$web = $site.rootweb
$pub = [Microsoft.SharePoint.Publishing.PublishingWeb]::GetPublishingWeb($web)

$pagesXML.Pages.Page | ForEach-Object {

  $fileName = $_.FileName
  $title = $_.Title
  $contentTypeId = $_.ContentTypeId
  $articleType = $_.ArticleType   #Choice fields just need a string value 
  $contact = Get-SPUser -Identity $_.Contact -web $web
  $reviewDate = [DateTime]::Now.AddDays($_.ReviewDate)
 #Below I replace a token in the XML to get the absolute url 
 #else it will error with subwebs, even if using relative with or without / 
  $pubPageLayout = $_.PubPageLayout -replace "{siteUrl}", $siteUrl

  Write-Host "Creating page $fileName"
  $newPage = $pub.AddPublishingPage()
  $newPage.ListItem["BaseName"] = $fileName
  $newPage.ListItem["Title"] = $title
  $newPage.ListItem["PublishingPageLayout"] = $pubPageLayout
 #Note how I have to set the CT as well, otherwise it uses an OOTB CT
 #even though it sets the Page Layout with one that may use a different CT?? 
  $newPage.ListItem["ContentTypeId"] = $contentTypeId
  $newPage.Update() #Must update here so we can access our custom columns
  $newPage.ListItem["Contact"] = $contact
  $newPage.ListItem["ArticleType"] = $articleType
  $newPage.ListItem["ReviewDate"] = $reviewDate
  $newPage.Update()
  $newPage.CheckIn("")
  $newPage.ListItem.File.Publish("")
  Write-Host "Published"
}
Write-Host "Completed!"
# Dispose
$web.Dispose()
$site.Dispose()

Now I need a XML structure that looks like this

pages.xml

 
<?xml version="1.0" encoding="utf-8"?>
<Pages>
 <Page>
 <FileName>Teampaintballcomp</FileName>
 <Title>Team paintball comp</Title>
 <PubPageLayout>{siteUrl}/_catalogs/masterpage/customPage.aspx,
  Custom Page</PubPageLayout>
 <ContentTypeId>0x010100C568DB52D9D0A14D9B2FDCC96666E9F2007948;</ContentTypeId>
 <Contact>SomeDomain\ovenss</Contact>
 <ArticleType>Sports</ArticleType>
 <ReviewDate>5</ReviewDate>
 </Page>
</Pages>

Note: I have left out any error checking in the script for the args for ease of reading. Please add if you use this in the wild!

SharePoint Online jQuery & jQuery UI WSP package

November 14, 2011 3 comments

Click here to download the jQuery sandbox WSP

With SharePoint online client side scripting really leads the way in which alot of solutions will be built, so bringing in the powerful library jQuery into your toolkit you will get much more out of the Solutions you build.

While there are a few solutions to get jQuery onto SharePoint Online the approach I have implemented here is a soution that adds jQuery and jQuery UI to all pages across the Site Collection using the Google API libraries which hosts the jQuery 1.6.4, jQuery 1.8.16, jQuery UI css start theme 1.8.13
(note: I will try and keep this package up to date with new releases of jQuery so if you see me slacking then give me a reminder).

To achieve this we cannot just add it to the CustomAction ScriptSrc or else it will just prepend a /_layouts/ to our absolute path.
You can use the ~SiteCollection token as shown below if you want to host it yourself but having it Google hosted it has advantages as talked about here and you would need to add the theme images and css files into a module in your package if self hosted.

<CustomAction
ScriptSrc=”~SiteCollection/Style Library/Scripts/jquery-ui-1.8.12.custom.min.js
Location=”ScriptLink” />

Solution
The solution is to load the scripts using the CustomAction ScriptBlock as follows:

<CustomAction
Location=”ScriptLink
ScriptBlock=”
            function loadjQueryScripts(src) {         
                      if (window.location.href.indexOf(‘AssetPortalBrowser.aspx’) > 0) return;
                      var head = document.getElementsByTagName(‘head’)[0];         
                      var script = document.createElement(‘script’);         
                      script.type = ‘text/javascript’;          
                      script.src = src;          
                      head.appendChild(script); 
            }

            function loadUIElements(){
                      if (window.location.href.indexOf(‘AssetPortalBrowser.aspx’) > 0) return;
                      var head = document.getElementsByTagName(‘head’)[0];
                      var cssNode = document.createElement(‘link’);
                      cssNode.type = ‘text/css’;
                      cssNode.rel = ‘stylesheet’;
                      cssNode.href = ‘https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/themes/start/jquery-ui.css&#8217;;
                      cssNode.media = ‘screen’;
                      head.appendChild(cssNode);
            }
            _spBodyOnLoadFunctionNames.push(‘loadjQueryScripts(\’https://&#8230;./jquery.min.js\’)’);
            _spBodyOnLoadFunctionNames.push(‘loadjQueryScripts(\’https://&#8230;./jquery-ui.min.js\’)’);
            _spBodyOnLoadFunctionNames.push(‘loadUIElements()’);
Sequence=”101“>
</CustomAction>

So as you can see above we are using the _spBodyOnLoadFunctionNames function to fire off our functions once the page has loaded, which in turn adds our jQuery libraries to the head tag.

Another benefit of having a global jQuery package is that you do not have to do any jQuery.js checking and/or registering in any of your other solutions that would use jQuery, just ensure you have this dependency installed.

It would be nice to see Microsoft build these options into their products, as products like DNN now have the option to tick a box to enable hosted jQuery. I am sure Microsoft wouldnt use the Google hosted libraries though 🙂

One thing to remember is that you should use the ExecuteOrDelayUntilScriptLoaded(yourFunctionName/plugin initialization, ‘sp.js’) if you use this package as you may recieve an error if you use the traditional jQuery $(function(){});
I believe this is due to the order the _spBodyOnLoadFunctionNames loads the jQuery.

Here is an example of a solution I built that requires jQuery being added to my Office 365 trial which simply changes the “more information” button to open in a nice popup rather then pushing the page down with a bad layout.

My takeaways from the Anaheim SharePoint 2010 Conference

October 15, 2011 Leave a comment

No I am not talking about the CHEAP fast food joints in the states that played on my wallet to get the biggest burger and drink for a ridiculously cheap price when compared to New Zealand.
I am sharing the sessions that I got something from so here is the list:

  1. Keynote
  2. SharePoint online Overview – Mark Kashman
  3. Out of the sandbox and into the cloud: Build your next SharePoint app on Azure – Andrew Connell
  4. Integrating SharePoint Social features into your Windows Phone 7 application – Todd Baginski
  5. SharePoint workflow best practises – Keenan Newton
  6. Developing SharePoint applications and HTML5 and jQuery – Ted Pattison
  7. Search Engine Optimization on a SharePoint 2010 Internet Site – Daniel Haywood
  8. Application lifecycle Management: Automated builds and testing for SharePoint projects – Chris O’Brien & Mike Morton
  9. Customising Content publishing approval workflows – Robert Bogue
  10. Branding SharePoint online Sites – Randy Drisgill & John Ross
  11. Deep Dive on SharePoint Ribbon Development & Extensibility. – Chris O’Brien & Andrew Connell
  12. Advance BI Visualization using Visio services. – Christopher Hopkins & A.J Briant

Keynote
The big things that came out of the keynote was the announcement that the next release to the cloud will include BCS, which should be released sometime end of this year I believe.

We were given a good demo of a full replicated failover of a 15 server farm that had a whopping 14 terabytes of data and how fast it happened, the only thing here was that the VM host they used had 80 cores with 1 terabytes of ram…..now that’s some computing power!

Also they mentioned the fact that the Microsoft SharePoint development team has increased dramatically and that they see the future of SharePoint to be big and are putting alot of focus into make it bigger and better. I know this seems obvious being their 3rd biggest seller, but its good to hear it coming from Microsoft’s mouth.

SharePoint online Overview – Mark Kashman

This was a basic intro into the cloud.  Demoing adding webparts that where utilising jQuery, and showing that it is basically the same as you see in the foundation version. 

He did demo the new BCS that is coming in the new year. You will be able to connect to webservices to allow the two way connection to your external data.

Out of the sandbox and into the cloud: Build your next SharePoint app on Azure – Andrew Connell

This was a great session by AC basically showing a good alternative to creating SharePoint applications in the cloud.
Using Azure as our data storage we can have full control on how we handle the data meaning no limitations on the structure of the data and accessing this data from various sites. Add the power of Microsoft’s Data Entity framework and WCF you can easy create your application that your SharePoint Online webparts can consume.

This would be a good option if you find yourself too limited by the site lists and what you can achieve with SharePoint Online. Also it gives greater flexibility for complex data and data relationships, meaning happier developers.

Integrating SharePoint Social features into your Windows Phone 7 application – Todd Baginski

Todd showcased a nice windows 7 phone application surfacing the social media data from SharePoint, using the OOTB SharePoint services and RSS feeds.

He also demoed the new push notification service available in mango. These are what you see in your home tiles showing you the number of new emails you have for example.
The notification service is hosted in the cloud and you as an application developer send a request to this service which then sends out the message to all phones subscribe to it (ie they have your application and have accepted to receive these messages). One caveat is that there is no guarantee that the message will make it to all phones.
Todd mentioned that it is complicated plumbing to get this to work, requiring a service to react to an event, which then sends a message to the notification service which in turn invokes an event on your phone application to which you can respond to by putting a count on your apps tile for example.
Todd’s push notification demo failed to appear on his phone and he did mentioned that when testing give it sometime before you blame your code as it can take a while for the push notification to send the message.

SharePoint workflow best practises – Keenan Newton

The main nugget I got from this was the idea of building custom actions developed in Visual Studio, which means you can get the best out of building workflows in SharePoint Designer but if you need it you have the power of custom code, in the form of a custom action, to give you exactly what you want.

Developing SharePoint applications and HTML5 and jQuery – Ted Pattison

One of my favourites. First Ted started with jQuery basics which almost made me leave but I knew he would dive deeper.
He demoed the jQuery templating engine that I have been using for over a year now so good to see others are using it in the wild. 
He demoed the library datajs which gives you a nice wrapper to easily post/put/delete data using OData services, I have yet to see why you would use this over the jQuery ajax library though.

He then moved onto HTML5 which I was amazed how clean and powerful it is. HTML5 has very readable tags like Nav, Article, Footer and so on which makes it nice to layout your HTML and it makes life easier for screen readers and so on.  Unfortunately its early days so not a lot of browsers support it, which means if you use it you will have to use a lot of fall back methods. Sure there are libraries that make this easy but the extra work required would have to be weighed up. I would say if its an internal web app and you can control the browser then go for HTML 5 otherwise it may not be the time yet to go all out.

One thing I see from this is that with HTML5 JavaScript  is going to be the main development language used,  just look at the ability to access the database straight from JavaScript, LocalStorage, web workers and web sockets….all i can say is awesome and bring it on!

Search Engine Optimization on a SharePoint 2010 Internet Site – Daniel Haywood

Daniel started with a good video on SEO to show how it all works, which showed the basics and that is that content is king and links from other high ranking sites give you a better ranking. But what I didnt know was that some search engines will ignore some links if you are using those sites that are designed to just provide a simple link back to your site, so if you pay for any of these type of sites then you maybe wasting your money.

He showed how you can use web master tools to see how well you are setup SEO wise, and how this tool can help you make your site better for search engines. It gives you info on how google sees your site, what keywords are being used to find your site and so on.

He also showed Waldeks custom field controls which basically give you the ability to add keywords, title and description to each page for use in SEO, very useful.

Application lifecycle Management: Automated builds and testing for SharePoint projects – Chris O’Brien & Mike Morton

My other favourite session. Chris and Mike showed how easy it is to set up continuous deployments that include the WSPs using Visual Studio. Also showed the workflow you use for continuous deployments and where you would call out to PowerShell to install the WSPs. They covered the fact that you can set it so that broken builds can not be checked in, instead they get shelved until the solution builds.
This was a really good session for me as in my current project we are using continuous deployments and while it was painful to begin with it has been an eye opener into how useful it is in a project with multiple developers.

Another cool thing they covered is Coded UI Tests which I like very much!  Basically it allows to record clicks (for example clicking a jQuery accordion) and then selecting the assertion (for example the accordion element should have a different class/style or certain text should appear. Once completed it will generate the code into your test class which can then be run by your continuous deployments. Very quick and easy to create, which is good considering some integration tests I had to write took alot of time.
One question was does this replace all other unit and integration tests and the answer was depends on your needs. Obviously more coverage is better so I see Coded UI tests great for the jQuery and other UI related testing, so I will start trying them out in the wild.

Customising Content publishing approval workflows – Robert Bogue

Robert showed that there is a bug with setting the approval status in a SharePoint Designer workflow on a pages library, it gives a message saying something around “file being checked out”, something I had previously encountered.  To get around this you create a custom action in visual studio to set the approval status, so yes a workaround needed!

Unfortunately for him he had a demo melt down and everything he tried to do to recover failed him, it was almost unbelievable the issued that all came together…poor guy.

Branding SharePoint online Sites – Randy Drisgill & John Ross

Randy and John showed basic branding starting from overriding CSS to using custom MasterPages. Good thing I got from this is not much is different from using foundation version, so life is good.

Deep Dive on SharePoint Ribbon Development & Extensibility. – Chris O’Brien & Andrew Connell

Chris and AC showed the different control options you can have in the ribbon, eg flyout buttons and so on.
They advised to always use your own custom group instead of adding to or changing an existing group as the idea of the ribbon is that it does not change, so buttons are always where you would expected them.

They talked about contextual menus, like the one used on media webpart, and the options for the contextual menu which are global and focused. Note contenxtual menus are not available to create in the sandbox.
Global means if you have multiple instances of the same webpart whatever action you do in the ribbon it will apply it to all the instances added.
Focused means the action you do in the ribbon will only apply to the webpart that is in focus, ie clicked on, this is how the media webpart is setup.
Both require working with OO JavaScript, and as AC said it is hard going.
In regards to focused contextual menus they both agreed that the complexity and plumbing of OO javascript to get this working may not be worth the trouble. Part of me is happy to hear this as I did look into how the media webpart works so I could do something similar and found it very hard, but the other part of me thinks why does it have to be so hard. Lets hope some smart cookie out there creates some wrappers to make it easier.

Advance BI Visualization using Visio services. – Christopher Hopkins & A.J Briant

Christopher and A.J had a very nice demo that used visual diagrams connected to a datasource, then published to sharepoint. I was impressed with the visual presentation of data, for example they had a map of the US and it displayed the businesses staff distribution and sales per state all in a nice visual diagram. I would enjoy looking at this rather then some excel spread sheet or lame old pie charts.
Also impressed on how easy it is to wire up using the Visio UI.

One failure was when he tried to use the diagram auto generatation in Visio and it failed to do anything, always a worry to see. Though they did mention that the auto genration will not always give good results anyway, so it sounds like the advice is to go straight to building up your custom diagram using the shapes provided.

That wraps up an excellent conference, networking and meeting some great people, talking and learning from the legends of SharePoint and learning some new areas of the product that is set to be in every organisation and keep us all busy for many years to come!

Rugby World Cup 2011 Windows phone app

May 8, 2011 7 comments

Features: Filter fixtures by country and venue. Set the default landing screen. See local pubs at each venue with map locations pinned. Latest Rugby World Cup 2011 news.  View all the locations on bing maps, its interesting to see the contrast between different country stadiums. You can toggle between road and satellite view and zoom in and out to see the stadium from a birds eye view.

You can trial out the app, so download it if you are following the rugby world cup and have a windows phone.

Here is the marketplace link: http://wp7.apphab.com/rugby-wc-11-fixtures-by-sointeractive/

Go the All Blacks!

Screen shots:

 

Mix the following ingredients jQuery AJAX, jQuery templating, SharePoint REST api and apostrophes?

February 28, 2011 Leave a comment

The mission:
I wrote a jquery plugin to get events from a calendar list on another site using the Sharepoint 2010 REST api.
So I thought the best approach was to use the $.getJSON() function to get the events as a javascript object, that way I could feed it into the jquery templating engine.

All went well until a tester used the dreaded apostrophe in an event title (surely we sorted this on out years ago:)).
What this meant is that the json object that was returned had an escape charactor in it, which according to the ecma v1 standards (reference: http://bclary.com) is the way to go:

Escape Sequence Code Point Value Name Symbol
\’ \u0027 single quote

Unfortunately the jQuery $.getJSON() failed.
Actually this is just a wrapper for the $.ajax() function which also failed.

The answer:
Use the dataFilter to override the jquery library and strip the escape ecma standard so that we can get our response back.

Here is the plugin:

;(function ($) {

    $.events = {
        VERSION: “0.1”,
        defaults: {
            url: “/eventshub/_vti_bin/ListData.svc/Events“,
            success: function (data, element) { 
                $(“#EventsTemplate”).tmpl(data.d).appendTo(element);
                element.accordion(); //note: dependency on jquery UI
            },
            template : ‘<script id=”EventsTemplate” type=”text/x-jquery-tmpl”>’ +
                            ‘{{each results}}’ +
                                ‘<h3>’ +
                                    ‘<a href=”#”>{{html Title}} ${$.events.formatDate(StartTime)}</a>’ +
                                ‘</h3>’ +
                                ‘<div>’ +
                                    ‘{{html Description}}’ +
                                    ‘<br /><a href=”${Path}/DispForm.aspx?ID=${Id}” target=”_blank”>-full event details-</a>’ +
                                ‘</div>’ +
                            ‘{{/each}}’ +
                        ‘</script>’
            },
            formatDate : function(date){  //note: dependency on dataTimeParser.js
              return new Date(parseInt(date.substr(6))).format(“d mmm”); 
           },
           title : function(url){
                return url.substr(url.indexOf(“, “) + 2); 
            },                
            url : function(url){
                return url.substr(0, url.indexOf(“,”)); 
            }
    };

    $.fn.extend({
        events: function(settings){

            settings = $.extend({}, $.events.defaults, settings);
           
            return this.each(function(){
                var $this = $(this);
                $(“head #EventsTemplate”).remove();
                $(“head”).append(settings.template);
                var url = settings.url + “?$filter=StartTime ge datetime'” + new Date().format(“yyyy-mm-dd’T’HH:mm:ss”) + “‘”;
                $.ajax({
                    url: url,
                    dataType: ‘json’,
                    success: function(data){
                                if ($.isFunction(settings.success)) settings.success(data, $this);
                              },
                    dataFilter: function(data, type){
                                    return data.replace(/\\’/g, “‘”); // fix for escaped apostrophes in the feed 
                             } 
                    });
            });
        }
    });

})(jQuery);

We can use it in a webpart or usercontrol like so:

<script type=”language/javascript” src=”https://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js”></script&gt;
<script type=”language/javascript” src=” https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js”></script>
<script type=”language/javascript” src=”{ path to your javascript }/jquery.tmpl.min.js”></script>
<script type=”language/javascript” src=”{ path to your javascript }/events.jquery.js”></script> <– our plugin above –>
<script type=”language/javascript”>

$(“.events”).events();

</script>

<div class=”events” />

Resources used:
jspec, a test suite for jquery, was used to write a series of unit tests.
Using firebug we can use the NET tab to get the json response to add as our test feed which is known as a test fixture.
This way we can override the url property in the plugin and give it our test data.
This gives us the abilty to have a number of possible scenarios both good and bad ensuring we have covered all the bugs testers may find (well maybe all :))

Connect webparts in SharePoint 2010

November 10, 2010 Leave a comment

Webpart connections in SharePoint 2010 have been somewhat simplified.
In this example I will take you through creating a webpart that shows a list of users that belong to the site and by selecting a user it will display their details.
In the second webpart (the consumer) it will display the selected user’s tasks. 

Completed solution view

So let’s get started by creating an empty SharePoint solution.
(Note: I tried a sandbox solution and while it did deploy it the option to connect the webparts was greyed out, unfortunate) 

Next we create an interface that will be used for the connection information between the provider and the consumer webpart.

namespace UsersInfoProjects.Code
{
    public interface IUserInfo
    {
        int UserId { get; }
    }
}  

In this example I am using the user id to pass between the provider and consumer webparts.

Now we have our interface let’s create our provider webpart.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using System.Linq;
using UsersInfoProjects.Code; 

namespace UsersInfoProjects.UsersInfo

//Inherit and implement our the interface
 
    [ToolboxItem(false)]
    public class UserInfo : WebPart, IUserInfo
    {
        protected DropDownList Users;
        protected Literal UserDetails;
        private SiteUsers _users; 

        protected override void OnLoad(EventArgs e)
        { 

//Here we are setting the user details so that they are displayed on page load and any additional postbacks

            if (Page.IsPostBack && int.Parse(Users.SelectedValue) != -1)
                UserDetails.Text = _users.GetFormattedDetails(int.Parse(Users.SelectedValue));
            else if (Page.IsPostBack && int.Parse(Users.SelectedValue) == -1)
                UserDetails.Text = string.Empty;
        } 

        protected override void CreateChildControls()
        {
            try
            { 

//Create an instance of our internal class called SiteUsers. This class is used to get the users names and details

               _users = new SiteUsers();
               Users = new DropDownList
                {
                    AutoPostBack = true,
                    DataSource = _users.GetUserNames(),
                    AppendDataBoundItems = true,
                    DataTextField = “Name”,
                    DataValueField = “ID”
                }; 

                Users.DataBind();
                UserDetails = new Literal();
                Controls.Add(Users);
                Controls.Add(UserDetails);
                Users.Items.Insert(0, new ListItem(“-Select colleague-“, “-1”));
            }

            catch (Exception ex)
            {
                Controls.Clear();
                Controls.Add(new LiteralControl(ex.Message));
            }
        } 

//Here is our property from our interface. We add a getter which returns the selected  value  

        public int UserId
        {
            get { return int.Parse(Users.SelectedValue); }
        } 

//Then we create a public method that has the attribute which marks it as the provider,  the string is what gets displayed when connecting the webparts through the UI as shown in the image below.

 

        [ConnectionProvider(“User ID”)]
        public IUserInfo UserInfoConnection()
        {
            return this;
        }
    } 

    internal class SiteUsers
    {
        private readonly List<SPUser> _users; 

        public SiteUsers()
        {
            _users = SPContext.Current.Web.SiteUsers.Cast<SPUser>().ToList();
        } 

        public static List<string> Hidden()
        {
            return new List<string> { @”NT AUTHORITY\authenticated users”, @”NT AUTHORITY\LOCAL SERVICE”, “System Account” };
        } 

        public List<SPUser> GetUserNames()
        {
            return _users.Where(x => !Hidden().Contains(x.Name)).ToList();
        } 

        public string GetFormattedDetails(int userId)
        {
            StringBuilder sb = new StringBuilder();
            _users.Where(x => x.ID == userId).ToList().ForEach(x =>
            {
                sb.AppendFormat(“<div>Email: {0}</div>”, x.Email);
                sb.AppendFormat(“<div>Notes: {0}</div>”, x.Notes);
            }); 

            return sb.ToString();
        }
    }
}

Now that we have completed the first webpart we could deploy it and use it as a standalone webpart.
But we want to create the consumer now. In the consumer webpart we need get the users tasks so let’s bring SPMetal into the mix.
First we create a folder to hold our SPMetal generator batch file as follows:

CD “C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\BIN”
SPMETAL.EXE /web:http://kcue-012 /code:C:\VSProjects\UsersInfoProjects\UsersInfoProjects\UsersInfoProxy.cs /namespace:UsersInfoProjects.UsersTasksConsumer
pause

Once we run this it will generate our class that we will then need to include into our project.
Now that we have this include we can use it in our webpart.

using System;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Linq;
using UsersInfoProjects.Code;

 namespace UsersInfoProjects.UsersTasksConsumer
{
    [ToolboxItemAttribute(false)]
    public class UsersTasksConsumer : WebPart
    { 

//Create a private member for our interface          

          private IUserInfo _provider;
      
         protected override void CreateChildControls()

        {
            if (_provider == null) return;
            StringBuilder sb = new StringBuilder(); 
 
//Create our context so we can run Linq to SharePoint
 
             UsersInfoProxyDataContext context = new UsersInfoProxyDataContext(SPContext.Current.Web.Url);
            context.ProjectTasks
                .Where(x => x.AssignedToId == _provider.UserId &&  x.Complete != 1)
                .OrderBy(x => x.DueDate)
                .ToList()
                .ForEach(x =>
                             {
                                 sb.AppendLine(“<div><strong>”);
                                 sb.AppendFormat(“<a href='{0}’>{1}</a>”, x.Path, x.Title);
                                 sb.AppendLine(” (“);
                                 if (x.DueDate.HasValue)
                                     sb.AppendFormat(” Due date: {0},”, x.DueDate.Value.ToString(“dd/MM/yyyy”)); 
                                     sb.AppendFormat(” Status: {0}”, x.TaskStatus);
                                     sb.AppendLine(” )</strong></div>”);
                             });

                   Controls.Add(new LiteralControl(sb.ToString()));
        } 

//Create the same public method that we created in the provider webpart only this time we create an attribute which marks it as the consumer.

         [ConnectionConsumer(“User ID”)]
         public void UserInfoConnection(IUserInfo providerInterface)
        {
            _provider = providerInterface;
        } 
    }

Now all that’s left is to deploy the solution. Add the webparts and connect them together.