Tags
Monday
Feb232015

Working with XML in Nintex Workflows (simplified)

 

1. Remove all the namespaces with XSLT transform.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">    
<xsl:template match="*">        
    <xsl:element name="{local-name()}">            
    <xsl:apply-templates select="@* | node()" />        
    </xsl:element>
</xsl:template>    
<xsl:template match="@*">        
    <xsl:attribute name="{local-name()}">            
    <xsl:value-of select="." />        
    </xsl:attribute>    
</xsl:template>    
<xsl:template match="text() | comment() | processing-instruction()">
    <xsl:copy />    
</xsl:template>
</xsl:stylesheet>

 

2. Force top level XML node.

This ensures if your service returns multiple top level elements they get wrapped together.

 

3. Use XPath in Query XML Action like this:

/xml/Results/Result/Title

 

Sometimes, after you've wasted half a day working with XML, you just want the simple, no frills, just make it work version.

This will hopefully help you too.

 

Big thanks to Jumpei Yamauchi for helping me with this one.

Thursday
Feb122015

Feb 2015 CU SP2013: Workflow CurrentSiteUrl is fixed

 

http://support.microsoft.com/KB/2920804

Assume that you create a SharePoint 2013 workflow by using SharePoint Designer 2013, and that the workflow contains an action that retrieves data from the "Current Site URL" field in the "Workflow Context" data source. When you run the workflow, the value retrieved from the "Current Site URL" field is missing a forward slash ("/") at the end of the URL.

 

A few months ago, Microsoft released an update to SharePoint 2013, and it broke many SharePoint Designer Workflows.

If you use the new capabilities in SharePoint 2013 to call REST services, it is very common to concatenate the URL using a combination of the Current Site Url as well as the api/ path.

"http://server/site/" + "_api/web/lists/getbytitle('Announcements')"

The problem is that Microsoft removed the trailing / in the Current Site Url.  This broke many REST calls.

"http://server/site" + "_api/web/lists/getbytitle('Announcements')"

Is no longer valid.  The fix is to reopen all your workflows that are showing this problem, and add a preceding / before _api.  Like this:

"http://server/site" + "/_api/web/lists/getbytitle('Announcements')"

 

Now, with the February 2015 patch, Microsoft is fixing the workflow action again.  Guess what.

"http://server/site/" + "/_api/web/lists/getbytitle('Announcements')"

 

I think this looks dirty

 

Technically, your workflow probably won't break over this.

"http://server/site//_api/web/lists/getbytitle('Announcements')"

Should return you the same values as

"http://server/site/_api/web/lists/getbytitle('Announcements')"

 

But it looks terrible.

Wednesday
Feb042015

SharePoint 2016 - debunking confusion and concerns

 

The announcement of SharePoint 2016 should not come as a surprise (Office 2016 was previously announced, and SharePoint has always been a product released in parallel).

http://blogs.office.com/2015/02/02/evolution-sharepoint/

What was the surprise, to me, is how many people immediately jump the gun and asks is there another version of SharePoint after 2016, I don't recall a time where when Microsoft announces Office 2010 is coming and people immediately ask is there an Office 2013.

 

Why the confusion

 

I think the confusion, or concern, is that customers can clearly see Microsoft's heavy investment in Office 365, and even the attitude of cannibalizing its own existing products to move forward. 

I think this is the right thing to do for Team Office.  Apple let iPhone ate the iPod.  Windows didn't evolve and got stagnant.

But our concern is genuine.  Many customers can not move to the cloud.  They are indeed worried whether they should continue spending in the on-premises product, or invest elsewhere.  It seems that Microsoft has not been investing in existing features, instead it has been investing only in Office 365 and very little is coming down the pike.

There are also much FUD spread by competitors implying Microsoft has abandoned on-premises and thus a customer should consider abandoning Microsoft and go with a competing on-premises product.

 

What we can safely assume

 

There are areas that Microsoft is playing to its strengths.  Many companies - Facebook for Work, Google Apps - are entirely cloud (or mobile) offerings.  SharePoint and Office 365 is a hybrid offering.  And in this strength, Microsoft is uniquely in the cloud, but also in your enterprise and cross-platform in your devices.  This isn't going to change, in fact, this is an area Microsoft will continue to expand the offering.

A decade ago, we can distil Microsoft and Windows down to A PC on Every Desktop

I'd like to think that for Team Office, it should be Office anywhere you do Work.

This means that perhaps while SharePoint the brand is fading into an on-premises only product, SharePoint the product is never going away.  It has not been given the 10-year support life line.  And Microsoft continues to invest in the product.

 

What could the next version of SharePoint look like?

 

I see two possibilities with the shape of the next version of SharePoint. 

It could be a stand alone product, in the shape of SharePoint 2019, launched with Office 2019 desktop suite.

It is also possible that in the SharePoint 2016 timeframe, the product becomes Evergreen and future updates are rolled out in the form of Service Packs.

 

I would kiss the Microsoft Product Manager that makes SharePoint On-Premises Evergreen.  This isn't something unimaginable.  Office 365 has already gone that route.  Windows 10 is envisioned as Windows as a Service and new updates will just roll out new features.  An Evergreen SharePoint effectively means this would be the last release version of SharePoint - and that is great for customers.

It's worthwhile to note that this isn't a decision Team Office has to make in the 2015-2016 timeframe.  This is a decision that should be made in the 2016-2018 timeframe.  By that time, Microsoft and customers would be well-understood about what Microsoft means when it says Windows As A Service.

 

So no rush.  But you know what I want, Dear Team-Office-Santa.

 

What I want to see more

 

http://blogs.microsoft.com/firehose/2015/02/02/how-sharepoint-will-evolve-in-the-cloud-with-office-365/

Reading between the lines in the Office announcement, I think Office 365/SharePoint announcements coming up in Ignite will be split into really three areas:

  • Office 365 / Cloud / SaaS - new features will appear here first.  That's what Cloud-First means.
  • SharePoint 2016 - boring backend updates, applicable learnings from Office 365 (probably not much - since we aren't big on hosting multi-tenanted environments in a single enterprise).  Database stuff.  High availability.  API updates and APPs that runs in the cloud but also on premises.
  • Hybrid, Hybrid, Hybrid - how to connect everything from Office 365 with On-premises SharePoint: Sway.  Video Portals.  The mystical "Next Generation Portals", Yammer, Groups, Delve and Hybrid Search. 

 

I expect news to be a bit light between now and Ignite conference, where Microsoft is storing up bags of product announcement goodies.

http://ignite.microsoft.com/

 

I'm preparing for everything to get more awesome.  And I don't think SharePoint is going away anytime soon.  If anything, it is still right in the middle of everything (if you are on the ground, not just in the cloud).

 

I'm Loving every bit of it.  Turn it up to v15!

Friday
Jan302015

Microsoft MVP Community Camp 2015

 

Microsoft MVPs in the Asia region continues with various events in 2015.  The first event is the MVP Community Camp held at the start of the year. 

 

Melbourne's starts "today" on January 30.

Sydney's will be the next Friday on February 6.

https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032610278&Culture=en-AU&community=0

There are two tracks:

MS Technologies for Business

 

In-depth with Azure

Register for free here

https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032610278&Culture=en-AU&community=0

While I won't be presenting a session, I'll be hanging around with the other MVPs across the Microsoft stack answering questions. 

 

Ask me anything about:

  • Office 365
  • SharePoint
  • TypeScript
  • JavaScript
  • WindowsPhone
  • Microsoft Band
  • XBox One
  • Windows 10
Wednesday
Jan282015

Quick - TSQL select XPath from XML Data Type with namespace

 

Examples on MSDN shows the full TSQL syntax for XPath query in a TSQL statement, this has been supported since SQL Server 2008.

SELECT CatalogDescription.query(' declare namespace PD="http://schemas.microsoft.com/sqlserver/2004/07/adventure-works/ProductModelDescription"; <Product ProductModelID="{ /PD:ProductDescription[1]/@ProductModelID }" /> ') as Result FROM Production.ProductModel

This returns:

<Product ProductModelID="19"/>
<Product ProductModelID="23"/>

Wildcard in XPath Query

This is a much easier syntax with Wildcards that isn't well know.

SELECT CatalogDescription.query('/*:ProductDescription[1]/@ProductModelID') as Result
FROM Production.ProductModel

This returns:

19
23

Plenty of people will tell you using Wildcard matches in XPath is the worst thing in the world.  But I think if you just want to do a quick query, this is fine.

Wednesday
Jan212015

So, this is what can happen if you hack SP javascript files

 

SharePoint 2010's April 2014 CU introduced a bug with copy and paste. 

Bform.debug.js 381,869 11-Feb-2014 12:37
Bform.js 249,012 30-Jan-2014 16:39
Form.debug.js 211,134 11-Feb-2014 12:37
Form.js 126,783 30-Jan-2014 16:39

 

This was fixed and released in SharePoint 2010's August 2014 CU.
http://support.microsoft.com/kb/2760757

Bform.debug.js 381,888 15-Jul-2014 11:17
Bform.js 249,022 15-Jul-2014 11:17
Form.debug.js 211,153 15-Jul-2014 11:17
Form.js 126,793 15-Jul-2014 11:17

 

Story

On December 10, 2014, before we patched to the August 2014 CU level.  A certain very naughty person (me) couldn't wait and decided to offer a quick hack to the Javascript for the users over the holidays.  He even did a bak backup file of the original Form.js

 

image

 

We have now applied the August patch, but unfortunately, you can see:

bform.js, bform.debug.js, form.debug.js all updated.

Form.js is NOT updated.

Copy and paste still doesn't work in our environment.

 

Oops.

 

While a cheating way would be that we install the patch on another environment, and copy the correct Form.js file over. 

We'll be experimenting in this dev environment to see what is the best way to revert and go back to the supported path.  Stay tuned for a follow up article.

Monday
Dec152014

SPD2013 Workflow - how to check user is member of group

 

I want to describe a method that I use to check if a user is a member of a group.

 

Steps

  • Call a REST webservice
    • Reference MSDN for the correct API
    • Build a RequestURL and a basic RequestHeader
    • Figure out what the results mean
  • Wrap it up in a Workflow Custom Activity

 

API

MSDN (http://msdn.microsoft.com/en-us/library/office/dn268594(v=office.15).aspx - this needs to be a SharePoint Developer's home page) documents a few REST end points that I use for this.

http://msdn.microsoft.com/en-us/library/office/dn531432(v=office.15).aspx#bk_Group

Says you can get to a sharepoint group via:

  • http://<site url>/_api/web/sitegroups(<group id>)
  • http://<site url>/_api/web/sitegroups(<group name>)

The group also has a Users property that points to a Users Collection.

http://msdn.microsoft.com/en-us/library/office/dn531432(v=office.15).aspx#bk_UserCollection

This expands our example to:

  • http://<site url>/_api/web/sitegroups(<group id>)/users

For example:

 

The Users Collection does not have a method for testing if a user exists.  So I've taken the shortcut and basically brute force the service and just try to retrieve a user.  If you try to request a user that doesn't exist in the collection, it will just error, and I just catch that error.

 

SharePoint Designer workflow

 

image

 

Build Request Header

image

Both Accept and Content-Type needs to be "application/json;odata=verbose"

 

Build Request URL

image

Concatenate Current Site URL (which ends without a trailing /) and the earlier API.

Note my group name is 'john Members'

 

Call Web Service

image

 

Catch and process the result value.

image

 

The ResponseCode could be either OK or InternalServerError

Get a property from the returned Response variable "d/Title" would correspond to the Display Name of the user returned.  If the ResponseCode was Error, then there would be no value in the Response object.

 

 

Sandbox Custom Workflow Activity

 

In Visual Studio, these activities can be bundled into one single Activity that can be reused in SharePoint Designer.  I'll update this in a future blog post on Visual Studio.

 

 

Thoughts on checking nested group or AD group memberships

  • There are no way to check member with nested groups.  One possibility is to not think of it as membership, but think of it as whether the person has a certain permission.

    Does the current user have permission to do Contribute on the current Site. 
  • A more complicated thinking could be to create a list, kick out everyone except the group you are interested in, and check if the current user has permission to that list.

 

 

This Example in JavaScript

 

The more I work with SharePoint 2013 Workflows the closer parallels I see relating to a traditional programming language.  Here's the same function call in Javascript.

var promise = $.ajax({
        type: "GET",
        url: _spPageContextInfo.siteServerRelativeUrl + "/_api/web/sitegroups/getbyname('john Owners')/users/getbyid(" + _spPageContextInfo.userId + ")",
        headers: {
                "accept": "application/json;odata=verbose"
        },
        contentType: "application/json;odata=verbose",
        dataType: "json",
        cache: false,
        processData: true
});

promise.then(
        function (data, status) {

                if (status == "success" && data && data.d) {
                        var title = d.Title;

                }
                else {
                    // success, but no records - this can't really happen.
                }
        },
        function () {
                // not successful - usually not a member of that group
        }
);

Thursday
Dec112014

TypeScript Definition file for SPServices v0.1

 

Here's something I have wanted to write for a long time, both as Thank-You to Marc Anderson's work on SPServices, as well as for personal learning of the TypeScript language.

 

Adding TypeScript Definition reference:

image

 

$().sp (intellisense)

image

 

$().SPServices.defaults.cacheXML

image

 

$().SPServices.Version()

image

 

$().SPServices.SPCascadeDropdowns({ ... })

image

 

And one more.

$().SPServices({ ... })

image

 

Syntax Error Detection that TypeScript does so well. 

Except for the error message.

Repeat after me in a ROBOT way.  "String is missing apply from type Function".

What it means is I was expecting a function why did I find a string. 

And because Javascript can be surprisingly retarded, TypeScript tries to accommodate and wonders: perhaps this string is a function in disguise - so let me look for the apply method on this object.  NOPE.  It is not a function.   /s

 

image

 

 

Bonus.  TypeScript understands SPServices returns a JQueryXHR promise object.

image

 

Wait.  Where's all the other methods?

 

This is v0.1...

 

Download

http://johnliu.net/storage/jquery.SPServices.d.ts

Also, how do I get this into Marc's repo?

Friday
Dec052014

SharePoint Saturday Sydney 2014

November 29, 2014 was a great day for SharePoint Saturday.

I presented "Develop and Build Workflow Apps in SP2013. Wait, Workflow Apps?" - which is a session that covers lots of the new things you can build with SharePoint 2013 workflows using primarily Visual Studio 2013.  These work on premises and in Office 365.

 

The REST end points opens up SharePoint

 

I still think my transition along with exploring new activity and suddenly jumping into REST was tough.  I need to work on how to introduce that point.

The key point stands.  As a developer, or even a power user using Workflows - the REST API opens SharePoint completely to me.  I can use it to create lists, site columns or assign permission groups.  The trouble is that creating the JSON packet to talk to SharePoint end point is quite hard.  Which is why the need (and the ability) to package existing series of Actions into a reusable Custom Activity is a big deal.

 

Confusion over Workflow Custom Activity

 

There was actually a lot of confusion over the artefact "Custom Activity".  In 2010, these were sandbox or coded solutions.  In 2013, Custom Activity is completely declarative.  It is a way for you to save a series of workflow actions into a reusable piece of functionality that you can use over and over.  In my demo project I have about 10 custom activities.

 

Downloads

 

 

News Update

 

We announced at the beginning and the end of SharePoint Saturday 2014 that this would be the last SharePoint Saturday in Australia.  As Microsoft and Industry trends towards Office 365, we will move with that trend.

From 2015 onwards, Office 365 Saturday will return, bigger and with more coverage of Office and SharePoint Online, but also address your On Premises needs.

Hope you have a great holidays and see you soon in 2015!

Friday
Nov282014

Style recommendation for writing CAML in C#

 

I came across some sample CAML code a while back, and have been following their way of defining CAML in C#

Today a colleague commented on my code and I thought, hey, I should write this down.

 

Use string.concat

 

var query = new SPQuery()
{
    Query = string.Concat(
                                "<Where>",
                                    "<Eq>",
                                    "<FieldRef Name='Parent' LookupId='true'/>",
                                    "<Value Type='Integer'>", id, "</Value>",
                                    "</Eq>",
                                "</Where>",
                                "<OrderBy>",
                                    "<FieldRef Name='DueDate' Ascending='TRUE' />",
                                    "<FieldRef Name=’Priority’ Ascending='TRUE' />",
                                "</OrderBy>")
};
var items = list.GetItems(query);

 

Benefits

 

  • You don't have to bury the CAML in one crazy line or block of text.
  • I can arbitrarily indent my XML elements and the code doesn't see it at all. 
  • You almost always need to insert some sort of variables in the middle of your CAML.  This way you don't have to rely on fancy string.format, or consider type casting.
  • You don't have to remember to add "..." + "..." at the end of every line. 
  • Don't like a chunk of XML?  You can easily line comment or region comment.

 

Try it out and see if this changes your life as it has for me.

(if you have been doing this for years and have blogged or seen this documented before, let me know where.)