Search-Based Global Navigation in SharePoint On-line

While migrating our Team Sites from SharePoint 2010 to SharePoint On-line we noticed that response time of the sites on SharePoint Online was significantly slower than SharePoint 2010. Researching it we discovered that the issue was caused by our use of structural navigation, and led us to this article about Navigation Options for SharePoint Online.

While the article makes the case that Search based navigation good   because it is security trimmed (as opposed to Managed Navigation), and fast (as opposed to Structural navigation), it states that Search based Navigation can only be implemented with a custom master page. This is incorrect.

I rewrote example script that the article provided to work with standard JavaScript (I removed the need for knockout) and to produce [hopefully] the exact same html that is produced by the out-of-the-box structural navigation provided by SharePoint.   The script is deployed to the SharePoint on-line site using the PnP Add-SPOJavascriptLink, and provides the same navigation experience as Structural Navigation in a fraction of the time.  (In my case response time wen from 5 seconds to under two seconds!)

Because the script produces the same navigation provided by the out of the box structural navigation, it works well with the PNP responsive-UI solution.   The only downside I see is that users cannot reorder the navigation Items,

The Script can be downloaded here and uploaded to a location on your tenant.

To install the script on your site collection run the command:

Add-SPOJavaScriptLink -Name “SearchbasedGlobalNav” -Url “url-of-the-uploaded-script” -Scope Site -Sequence 500

 

Posted in Uncategorized | Leave a comment

Powershell Script to move documents between sitecollections with version history

##see https://shannonbray.wordpress.com/2010/06/26/moving-sharepoint-2007-sites-to-another-environment-with-powershell/

Add-PSSnapin microsoft.sharepoint.powershell -ErrorAction SilentlyContinue

[void][System.Reflection.Assembly]::LoadWithPartialName(“Microsoft.SharePoint.Deployment”)

$fileName = “exportedWeb.cmp”

$fileLocation = “c:export”

$SourceSiteURL = http://edmsqa.kznsands.local/sites/CP”

$docid=“CPDOC-46-30”

$DestinationSiteURL = http://edmsqa.kznsands.local/sites/cpccold/”

$DestinationWebName = “MPPProd” # need to add logic to use rootweb if this is null

$DestinationLibraryName=‘Documents’

##############################################The Export Script

$SourceSite = get-spsite $SourceSiteURL

$provider = [Microsoft.Office.DocumentManagement.DocumentId]::GetProvider($SourceSite)

$result = $provider.GetDocumentUrlsById($SourceSite, $docid)

$sourceWeb = $SourceSite.RootWeb

$file = $sourceWeb.GetFile($result[0])

$item=$file.Item

# Create a new SPExportObject

$exportObject = new-object Microsoft.SharePoint.Deployment.SPExportObject

# Populate the SPExportObject properties

$exportObject.Id = $item.UniqueId;

#$exportObject.IncludeDescendants = [Microsoft.SharePoint.Deployment.SPIncludeDescendants]::All

$exportObject.Type = [Microsoft.SharePoint.Deployment.SPDeploymentObjectType]::File

# Create a new object that holds all of the settings

$settings = new-object Microsoft.SharePoint.Deployment.SPExportSettings

$settings.SiteUrl = $SourceSiteURL

$settings.ExportMethod = [Microsoft.SharePoint.Deployment.SPExportMethodType]::ExportAll

$settings.FileLocation = $fileLocation

$settings.FileCompression = $false

$settings.BaseFileName = $fileName

$settings.ExcludeDependencies = $false

$settings.ExportObjects.Add($exportObject)

$settings.OverwriteExistingDataFile = $true

$settings.IncludeVersions = [Microsoft.SharePoint.Deployment.SPIncludeVersions]::All

# Create a new SPExport object based off the the settings of the SPExportSettings object

$export = new-object Microsoft.SharePoint.Deployment.SPExport($settings)

# Start the export

$export.Run()

# Dispose of the site object to release it from memory

$site.Dispose

#######################################################IMPORT

# Create a new object that holds all of the settings

$settings = new-object Microsoft.SharePoint.Deployment.SPImportSettings

$settings.SiteUrl = $DestinationSiteURL

$settings.FileLocation = $fileLocation

$settings.FileCompression = $false

$settings.RetainObjectIdentity = $false

$settings.UserInfoDateTime = [Microsoft.SharePoint.Deployment.SPImportUserInfoDateTimeOption]::ImportAll

$settings.BaseFileName = $fileName

$settings.CommandLineVerbose=$true

$import = new-object Microsoft.SharePoint.Deployment.SPImport($settings)

$destinationSite = Get-SPSite $destinationSiteURL

$destinationWeb=$destinationSite.OpenWeb($DestinationWebName)

$destinationList= $destinationWeb.Lists[$DestinationLibraryName]

$import.add_Started({ # change the ParentUrl to the root folder of the Targhet LIst

param($Source, $EventArgs)

$rootObject = $EventArgs.RootObjects

ForEach ($importObject in $rootObject) {

if ($importObject.Type -eq [Microsoft.SharePoint.Deployment.SPDeploymentObjectType]::File -or $importObject.Type -eq [Microsoft.SharePoint.Deployment.SPDeploymentObjectType]::ListItem) {

$importObject.TargetParentUrl = $destinationList.RootFolder.ServerRelativeUrl

}

}

})

$import.Run()

 

Posted in Uncategorized | Leave a comment

An AngularJS directive for SharePoint Lookup Columns

Recently I’ve been researching how I can use AngularJS in my SharePoint 2010 environment. Specifically I’ve been working on using the Angular UI-Grid to edit SharePoint lists. I was able to edit most of the columns of  a standard Task list without too much effort, but the ‘Predecessors’ column which is a Lookup column that allows multiple selections was troublesome. I ended up needing to create an AngularJS directive that allows me to work with SharePoint lookup columns (which I am documenting here).  Having gone through that exercise, I’m now envisioning directives to work with all the different column types(sp-lookup, spdate,sp-choice…), directives that display SharePoint views and forms(sp-view,spform,sp-nav), etc.  These directives could even be driven off of the site metadata.

The file spLookup.js defines a module that contains my ‘spLookup directive and a supportingspFieldsDataService Service (spLookup.js can be downloaded from my onedrive here).

The directive is designed to render a Lookup column from a list that was retrieved by calling listdata.svc. If the lookup is a multi-select, you must expand the lookup column when you make your original rest call  to listdata.svc (otherwise the spLookup directive would need to go get the values of the lookup column for each row rendered).It does not work with CSOM. I haven’t tested yet using the SharePoint 2013 _Api endpoint, but it should work.

Here’s how it’s used:

<sp-lookup

controlMode=”edit|new|display”

ng-model=”FieldInTheScope”

multiple

lookupListUrl=”urlOfLookupList”

lookupField=”ColumnToDisplayInTheUI”

modelField=”TheIDColumn”

loadAll=”true”>

</sp-lookup>

The controlMode attribute states whether the control should display in edit, new, or display mode, like it does on the server side. In display mode, the control will just display the values(s) of the lookupField. In edit or new mode the control will display a select control.

The ng-Model attribute names a field on the scope to bind to.

The multiple attribute, if present, allows the user to select multiple values.

The lookupListUrl attribute is the url of the list that is used to display the select box (I also need to add an alternative attribute ‘lookupList’ which could be used if you already have the lookup list in your model, or maybe you want to narrow down the selections).

The lookupField attribute is the name of the field to display in the select control.

The modelField attribute is the name of the field to store in the model for item(s) that are selected. (I should probably just default this to ‘Id’)

The loadAll attribute states whether the sp-lookup control should load all values from the lookupListUrl when it is initialized. If you have no intention of editing any list items, and therefor will not need to render a select control, you can set this to false (I should probably just default this to true!).

To see the control in action create a standard Task list on one of your sites, and add a few tasks to it. Be sure to add a few Predecessors and an Assigned To to each task .In Sharepoint OnLine/Sharepoint 2013  you may need to click the Show more link at the botom of the  form to see the Predecessors column. Also In Sharepoint OnLine/Sharepoint 2013  change the Assigned To column to not Allow multiple selections for this demo.

Upload spLookup.js, spLookupDemo.js,  and spLookupDemo.html into your Site Assets library from here. Also Download angular.js and add it to your Site Assets Library.

For SharePoint 2010:

Create a web-part page to use to test the control. (Site Actions–>More Options–>Page–>Web Part Page )

Add the following scripts to the ContentPlaceholderAdditionalPageHead:

<script type=’text/javascript’ src=’/SiteAssets/angular-1.2.23/angular.js’></script>

<script  src=”/SiteAssets/splookup.js” type=”text/javascript”></script>

<script  src=”/SiteAssets/splookupdemo.js” type=”text/javascript”></script>

You’ll need to adjust the urls based on your environment.

And replace the contents of the ContentPlaceholderMain with this:

<div data-ng-app=”app1″ data-ng-controller=”spLookupDemoCtrl”>

<select size=”5″  ng-options=”Task as Task.Title for Task in tasks”  data-ng-model=”selectedTask”>  </select>

<div ng-show=”selectedTask”>

<table>

<caption>{{selectedTask.Title}}</caption>

<tr>

<td>Predecessors (multi-select) with controlMode display:</td>

<td>

<div data-sp-lookup controlMode=”display” ng-model=”selectedTask.Predecessors.results” multiplelookupListUrl=”../../_vti_bin/listdata.svc/Tasks”

lookupField=”Title” modelField=”Id” loadAll=”true”/>

</td>

.

</tr>

<tr>

<td>Predecessors (multi-select) with controlMode edit:</td>

<td>

<div data-sp-lookup controlMode=”edit” ng-model=”selectedTask.Predecessors.results” multiplelookupListUrl=”../../_vti_bin/listdata.svc/Tasks”

lookupField=”Title” modelField=”Id” loadAll=”true”/>

</td>

</tr>

<tr>

<td>Assigned To (single-select) with controlMode display:</td>

<td>

<div data-sp-lookup controlMode=”display” ng-model=”selectedTask.AssignedToId”lookupListUrl=”../../_vti_bin/listdata.svc/UserInformationList”

lookupField=”Name” modelField=”Id” loadAll=”true”/>

</td>

</tr>

<tr>

<td>Assigned To (single-select) with controlMode edit:</td>

<td>

<div data-sp-lookup controlMode=”edit” ng-model=”selectedTask.AssignedToId” lookupListUrl=”../../_vti_bin/listdata.svc/UserInformationList”

lookupField=”Name” modelField=”Id” loadAll=”true”/>

</td>

</tr>

</table>

<a href=”” ng-click=”saveTask()”>save</a>

</div>

</div>

This html can be copied from the spLookupDemo.html file in the download directory above.

For  Office 365:

Click the Site Actions Gear in the upper right and select Add a Page. Edit the Page, the click the Insert tab, and select ‘Embed Code’

Add the following:

<script src=’../SiteAssets/angular.js’ type=’text/javascript’></script>
<script src=’../SiteAssets/splookup.js’ type=’text/javascript’></script>
<script src=’../SiteAssets/splookupDemo.js’ type=’text/javascript’></script>

Then,  add a Content Editor webpart and point it to ‘SiteAssets/splookupDemo.html’

If all works well, the results will be a sample page demonstrating the use of this directive with both Single and Multi-select columns in both Edit and display mode:

Sosplookupdemo

Some features I need to add to this are pulling templates from the templateCache and allowing them to be overridden,  a lot of error checking, and the ability to handle lookups with fill-in choices.

Posted in Uncategorized | Tagged , , , , , | 4 Comments

AngularJS displaying ng-grid group information inside the grid as in a treeGrid

I had a need to group information in an ng-grid by more than one column (cname, maturity, Buy/Sell and Price), and to display summary data for each group within the grid(not in the ‘label’ format that ng-grid normally uses for aggregate information).

The data coming from my back-end (after my transformResponse) was an array of items that looked like this:

{
“identid”: “100001”,
“cname”: “BA”,

“maturity”: “2014-11-05T05:00:00Z”,

“bs”: “B”,

“price”: “121.19”,

“lots”: 1,

“buylots”: 1,

“selllots”: 0

}

So, in order to aggregate by those multiple fields, i first needed to create a computed field to group by , and then sort the array by that field(in this case using underscore):

_.each(tradelist, function(trade) {
trade.groupCol = trade.cname + trade.maturity + trade.bs + trade.price;
});

$scope.griddata =_.sortBy(tradelist, function(row) {
return row.groupCol;
});

Then I set my gridoptions to group by the newly created groupCol, but not show it:

    $scope
data: griddata
showFilter: false,
width: ‘572px’,
showGroupPanel: false,
groups: [‘groupCol’],
enableRowSelection: true,
multiSelect: false,
showSelectionCheckbox: true,
columnDefs: [
{ field: ‘groupCol’, visible: false },
{ field: ‘cname’, displayName: ‘Contract’, width: ‘150px’ },
{ field: ‘maturity’, displayName: ‘Maturity’, width: ‘112px’, cellFilter: ‘date:\’dd-MMM-yy\” },
{ field: ‘bs’, displayName: ‘B/S’, width: ’50px’ },
{ field: ‘price’, displayName: ‘Price’, width: ‘100px’ },
{ field: ‘identid’, displayName: ‘Trades’, width: ‘100px’ },
{ field: ‘lots’, displayName: ‘Lots’, width: ’60px’ }
]

};

Now the grid was grouping by my combined column, but was displaying the aggregate info in the standard ng-grid format( just a <span> tahg) , not in the grid. What I needed was to display the group information within  the grid, displaying the total of all Lots in the Lots columns and a count of the trades in the trade column.

I first created the function to aggregate the Lots:

$scope.aggregateLots = function(row) {

var lots = 0;

angular.forEach(row.children, function(subrow) {

lots += subrow.entity.lots;

});

return lots;

}

Then I changed the gridOptions.aggregateTemplate to display the data from the first row for each of the grouped columns, and summary data for the Lots and Trade columns:

$scope.gridOptions.aggregateTemplate:
“<div ng-click=\”debugger;customExpand(row,gridId)\” ng-style=\”rowStyle(row)\” class=\”ngAggregate\”>\r” +

“\n” +
“<div class=’ngCell col3 col3t’><div class=’ngCellText col3 col3t’>{{row.children[0].entity.cname}}</div></div>\r\n” +

“<div class=’ngCell col4 col4t’><div class=’ngCellText col4 col4t’>{{row.children[0].entity.maturity | date:\’dd-MMM-yy\’ }}</div></div>\r\n” +

“<div class=’ngCell col5 col5t’><div class=’ngCellText col5 col5t’>{{row.children[0].entity.bs}}</div></div>\r\n” +

“<div class=’ngCell col6 col6t’><div class=’ngCellText col6 col6t’>{{row.children[0].entity.price}}</div></div>\r\n” +

“<div class=’ngCell col7 col7t’><div class=’ngCellText col7 col7t’>{{row.totalChildren()}}</div></div>\r\n” +

“<div class=’ngCell col8 col8t’><div class=’ngCellText col8 col8t’>{{aggregateLots(row)}}</div></div>\r\n” +

“\n” +

“<div class=\”{{row.aggClass()}}\”></div>\r” +
“\n” +
“</div>\r” +
“\n”;

The aggregateTemplate  displays the summary data using the same classes as the detailed rows so that the summary data appears as if it were in the grid:

ng-gridSummarized

A working version with complete code can be found at http://plnkr.co/edit/FQepdgFKtYqAVilvEwXZ?p=preview

Posted in angular, ng-grid | Tagged , , , , , , , | 1 Comment

Making Angular ng-grid expand only one group

I had an ng-grid set up with a single ‘group’ column set in the gridOptions. I needed to make it so that only a single group is expanded at any time (i.e. when a user clicked to expand a group,the other groups got automatically collapsed) and that the expanded group was automatically scrolled to the Top of the view.

I created a custom aggregateTemplate in my gridoptions and changed the ng-click to call a custom function to collapse all the other groups before expanding the one just clicked:

$scope.gridOptions = {

aggregateTemplate: “<div ng-click=”customExpand(row)” ng-style=”{‘left’: row.offsetleft}” class=”ngAggregate”><span class=”ngAggregateText”>{{row.label CUSTOM_FILTERS}} ({{row.totalChildren()}} {{AggItemsLabel}})</span><div class=”{{row.aggClass()}}”></div></div>”,
showGroupPanel: false,
groups: [‘groupCol’],….

The customExpand function looks like this:

$scope.customExpand = function (row) {
// first collapse all others
angular.forEach($scope.gridOptions.ngGrid.rowFactory.aggCache, function (aggRow, key) {
if (aggRow.isAggRow && aggRow.collapsed == false) {
aggRow.toggleExpand();
}
});
// then scroll to row for this group
var rowNum = 0;
angular.forEach($scope.gridOptions.ngGrid.rowFactory.aggCache, function (aggRow, key) {
if (aggRow.label == row.label) {
rowNum = key;
}
});
$scope.gridOptions.ngGrid.$viewport.scrollTop(rowNum * $scope.gridOptions.ngGrid.config.rowHeight);
// then expand the row
row.toggleExpand();
}

Now, only a single group is expanded at one time. and the expanded group scrolls to the top of the view.

Posted in angular, ng-grid | Tagged , , | Leave a comment

Rendering JSON from an XSLTListViewWebPart

I recently built a site on SharePoint 2010 that used lots of JavaScript for custom form validations.  The JavaScript made ajax calls back to listdata.svc to get the data to perform the validations. Things worked fine locally, but when a user from the other side of the globe accessed the site, things were slow.  A quick test using Fiddler’s ‘TimeLine’ tab showed me that after the initial page load, seconds were being spent making  multiple  ajax calls to get the validation data.

My first thought was to switch from rest to csom so that I could batch all the requests together and get the validation data in a single call. But then I began to think ‘Why not just send it down with the initial page?’ I thought about writing a custom webpart that could get data from an SPDataSource and serialize it to JSON and write it to the output stream.

Then it occurred to me that this could probably be done using an XSLTListViewWebPart and some custom XSL.  I found several pages that discussed serializing XML to JSON (Most notably http://controlfreak.net/xml-to-json-in-xslt-a-toolkit/ ).  Tweaking one of the samples in that article gave me the following XSLT which renders an XSLTListViewWebPart’s data into the browser:

<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform&#8221;
version=”1.0″ xmlns:ddwrt2=”urn:frontpage:internal”>
<xsl:param name=”webpartTitle”></xsl:param><!– need to add parameter binding <ParameterBinding Name=”webpartTitle” Location=”WPProperty(Title)” /> –>
<!– Turn off auto-insertion of <?xml> tag and set indenting on –>
<xsl:output method=”text” encoding=”utf-8″ indent=”yes”/>

<!– strip whitespace from whitespace-only nodes –>
<!–CAUSES ERROR Error while executing web part: System.Xml.Xsl.XslTransformException: White space cannot be stripped from input documents that have already been loaded. Provide the input document as an XmlReader instead. –>
<!– <xsl:strip-space elements=”*”/> –>

<xsl:template match=”/dsQueryResponse”>
<xsl:value-of select=”$webpartTitle” />
<xsl:text>&lt;script type=&quot;text/javascript&quot; &gt; debugger;</xsl:text>
<xsl:value-of select=”$webpartTitle” />
<xsl:text>=</xsl:text>

<xsl:apply-templates></xsl:apply-templates>
<xsl:text>&lt;/script&gt;</xsl:text>
</xsl:template>

<!– handles elements –>
<xsl:template match=”*”>
<!– element name –>
<xsl:text>{ &quot;e&quot; : &quot;</xsl:text>
<xsl:value-of select=”name()”/>
<xsl:text>&quot;</xsl:text>
<xsl:variable name=”ctr” select=”count(*)”/>
<xsl:variable name=”actr” select=”count(@*)”/>
<xsl:variable name=”tctr” select=”count(text())”/>
<!– there will be contents so start an object –>
<xsl:if test=”$actr > 0″>
<xsl:text>, &quot;values&quot; : { </xsl:text>
<xsl:apply-templates select=”@*”/>
<xsl:text>}</xsl:text>
</xsl:if>
<!– handle element nodes –>
<xsl:choose>
<xsl:when test=”$ctr = 1″>
<xsl:text>, &quot;data&quot; : </xsl:text>
<xsl:apply-templates select=”*”/>
</xsl:when>
<xsl:when test=”$ctr > 1″>
<xsl:text>, &quot;rows&quot; : [ </xsl:text>
<xsl:apply-templates select=”*”/>
<xsl:text> ]</xsl:text>
</xsl:when>
</xsl:choose>
<!– handle text nodes –>
<xsl:choose>
<xsl:when test=”$tctr = 1″>
<xsl:text>, &quot;$&quot; : </xsl:text>
<xsl:apply-templates select=”text()” />
</xsl:when>
<xsl:when test=”$tctr > 1″>
<xsl:text>, &quot;$&quot; : [ </xsl:text>
<xsl:apply-templates select=”text()” />
<xsl:text> ]</xsl:text>
</xsl:when>
</xsl:choose>
<xsl:text> }</xsl:text>
<xsl:if test=”position() != last()”>
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:template>

<!– this template handle text nodes –>
<xsl:template match=”text()”>
<xsl:variable name=”t” select=”.” />
<xsl:choose>
<!– test to see if it is a number –>
<xsl:when test=”string(number($t)) != ‘NaN'”>
<xsl:value-of select=”$t”/>
</xsl:when>
<!– deal with any case booleans –>
<xsl:when test=”translate($t, ‘TRUE’, ‘true’) = ‘true'”>
<xsl:text>true</xsl:text>
</xsl:when>
<xsl:when test=”translate($t, ‘FALSE’, ‘false’) = ‘false'”>
<xsl:text>false</xsl:text>
</xsl:when>
<!– must be text –>
<xsl:otherwise>
<xsl:text>&quot;</xsl:text>
<xsl:value-of select=”$t”/>
<xsl:text>&quot;</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:if test=”position() != last()”>
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:template>

<!– this template handles attribute nodes –>
<xsl:template match=”@*”>
<!– attach prefix to attribute names –>
<xsl:text>&quot;</xsl:text>
<xsl:value-of select=”name()”/>
<xsl:text>&quot; : </xsl:text>
<xsl:variable name=”t” select=”.” />
<xsl:choose>
<xsl:when test=”string(number($t)) != ‘NaN'”>
<xsl:value-of select=”$t”/>
</xsl:when>
<xsl:when test=”translate($t, ‘TRUE’, ‘true’) = ‘true'”>
<xsl:text>true</xsl:text>
</xsl:when>
<xsl:when test=”translate($t, ‘FALSE’, ‘false’) = ‘false'”>
<xsl:text>false</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>&quot;</xsl:text>
<xsl:value-of select=”$t”/>
<xsl:text>&quot;</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:if test=”position() != last()”>
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:template>

</xsl:stylesheet>

The XSLT creates a JavaScript global variable with the same name as the webpart and writes the json data into that variable. Note that I needed to add an additional Parameter Binding:

<ParameterBinding Name=”webpartTitle” Location=”WPProperty(Title)” />

to the webpart so that the JavaScript variable could be named the same thing as the Webpart Title.  Also note that after the webpart is added to the page the ‘Enable Asynchronous Load’ option must be turned off, otherwise the JSON object will be written into an IFrame, and not easily accessible by the scripts on the main page.

Now I can just place these modified XSLTListViewWebParts on my forms page and the validation data I was previously retrieving via ajax calls is delivered with the initial page load and can be accessed using my custom JavaScript. No more ajax callbacks!

As an added benefit I can now filter the data send to the browser by editing the XSLTListViewWebParts, adding/removing columns, setting filter criteria etc.

Other possible uses of this technique could be to create views completely in JavaScript or the XSLT could easily be modified to write out Knockout observables. Also, if you Group the data in the Webpart, group data is even written to the JSON so you could render the groups on the client!

Below is the full source code for a webpart that serializes its data to JSON (Note that you should probably store the XSL in a library rather than inline)

<WebPartPages:XsltListViewWebPart runat=”server” Description=”mapping for contracts” PartOrder=”2″ HelpLink=”” AllowRemove=”True” IsVisible=”True” AllowHide=”True” UseSQLDataSourcePaging=”True” ExportControlledProperties=”False” IsIncludedFilter=”” DataSourceID=”” Title=”MAP_Contracts” ViewFlag=”8388613″ AllowConnect=”True” DisplayName=”JSON” FrameState=”Normal” PageSize=”-1″ PartImageLarge=”” AsyncRefresh=”False” ExportMode=”NonSensitiveData” Dir=”Default” DetailLink=”/sites/testing/sitenamehere/Lists/MAP_Contracts” ShowWithSampleData=”False” ListId=”7f481096-9449-48bd-b02d-e3b8dc62ef95″ ListName=”{7F481096-9449-48BD-B02D-E3B8DC62EF95}” FrameType=”Default” PartImageSmall=”” IsIncluded=”True” SuppressWebPartChrome=”False” AllowEdit=”True” ViewGuid=”{A3EE0A01-B7EB-407E-9FC3-4572CB34B207}” AutoRefresh=”False” AutoRefreshInterval=”60″ AllowMinimize=”True” WebId=”00000000-0000-0000-0000-000000000000″ ViewContentTypeId=”0x” InitialAsyncDataFetch=”False” GhostedXslLink=”main.xsl” MissingAssembly=”Cannot import this Web Part.” HelpMode=”Modeless” ID=”g_a3ee0a01_b7eb_407e_9fc3_4572cb34b207″ ConnectionID=”00000000-0000-0000-0000-000000000000″ AllowZoneChange=”True” TitleUrl=”/sites/testing/sitenamehere/Lists/MAP_Contracts” ManualRefresh=”False” __MarkupType=”vsattributemarkup” __WebPartId=”{A3EE0A01-B7EB-407E-9FC3-4572CB34B207}” __AllowXSLTEditing=”true” __designer:CustomXsl=”fldtypes_Ratings.xsl” WebPart=”true” Height=”” Width=””><ParameterBindings>
<ParameterBinding Name=”webpartTitle” Location=”WPProperty(Title)” />
<ParameterBinding Name=”dvt_sortdir” Location=”Postback;Connection” />
<ParameterBinding Name=”dvt_sortfield” Location=”Postback;Connection” />
<ParameterBinding Name=”dvt_startposition” Location=”Postback” DefaultValue=”” />
<ParameterBinding Name=”dvt_firstrow” Location=”Postback;Connection” />
<ParameterBinding Name=”OpenMenuKeyAccessible” Location=”Resource(wss,OpenMenuKeyAccessible)” />
<ParameterBinding Name=”open_menu” Location=”Resource(wss,open_menu)” />
<ParameterBinding Name=”select_deselect_all” Location=”Resource(wss,select_deselect_all)” />
<ParameterBinding Name=”idPresEnabled” Location=”Resource(wss,idPresEnabled)” />
<ParameterBinding Name=”NoAnnouncements” Location=”Resource(wss,noXinviewofY_LIST)” />
<ParameterBinding Name=”NoAnnouncementsHowTo” Location=”Resource(wss,noXinviewofY_DEFAULT)” />
</ParameterBindings>
<XmlDefinition>
<View Name=”{A3EE0A01-B7EB-407E-9FC3-4572CB34B207}” MobileView=”TRUE” Type=”HTML” DisplayName=”JSON” Url=”/sites/testing/GeraldFixPortal/Lists/MAP_Contracts/JSON.aspx” Level=”1″ BaseViewID=”1″ ContentTypeID=”0x” ImageUrl=”/_layouts/images/generic.png”>
<Method Name=”Read List”/>
<Query>
<GroupBy Collapse=”TRUE” GroupLimit=”30″>
<FieldRef Name=”Exchange”/>
</GroupBy>
<Where>
<Eq>
<FieldRef Name=”Category”/>
<Value Type=”Text”>CME_Futures</Value>
</Eq>
</Where>
</Query>
<ViewFields>
<FieldRef Name=”ID”/>
<FieldRef Name=”Category”/>
<FieldRef Name=”Cname”/>
</ViewFields>
<RowLimit Paged=”TRUE”>2000000</RowLimit>
<Aggregations Value=”Off”/>
<Toolbar Type=”Standard”/>
</View>
</XmlDefinition>
<DataFields>
</DataFields>
<Xsl>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform&#8221;
version=”1.0″ xmlns:ddwrt2=”urn:frontpage:internal”>
<xsl:param name=”webpartTitle”></xsl:param><!– need to add parameter binding <ParameterBinding Name=”webpartTitle” Location=”WPProperty(Title)” /> –>
<!– Turn off auto-insertion of <?xml> tag and set indenting on –>
<xsl:output method=”text” encoding=”utf-8″ indent=”yes”/>

<!– strip whitespace from whitespace-only nodes –>
<!–CAUSES ERROR Error while executing web part: System.Xml.Xsl.XslTransformException: White space cannot be stripped from input documents that have already been loaded. Provide the input document as an XmlReader instead. –>
<!– <xsl:strip-space elements=”*”/> –>

<xsl:template match=”/dsQueryResponse”>

<xsl:text>&lt;script type=&quot;text/javascript&quot; &gt; debugger;</xsl:text>
<xsl:value-of select=”$webpartTitle” />
<xsl:text>=</xsl:text>

<xsl:apply-templates></xsl:apply-templates>
<xsl:text>&lt;/script&gt;</xsl:text>
</xsl:template>

<!– handles elements –>
<xsl:template match=”*”>
<!– element name –>
<xsl:text>{ &quot;e&quot; : &quot;</xsl:text>
<xsl:value-of select=”name()”/>
<xsl:text>&quot;</xsl:text>
<xsl:variable name=”ctr” select=”count(*)”/>
<xsl:variable name=”actr” select=”count(@*)”/>
<xsl:variable name=”tctr” select=”count(text())”/>
<!– there will be contents so start an object –>
<xsl:if test=”$actr > 0″>
<xsl:text>, &quot;values&quot; : { </xsl:text>
<xsl:apply-templates select=”@*”/>
<xsl:text>}</xsl:text>
</xsl:if>
<!– handle element nodes –>
<xsl:choose>
<xsl:when test=”$ctr > 1″>
<xsl:text>, &quot;rows&quot; : [ </xsl:text>
<xsl:apply-templates select=”*”/>
<xsl:text> ]</xsl:text>
</xsl:when>
</xsl:choose>
<xsl:text> }</xsl:text>
<xsl:if test=”position() != last()”>
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:template>

<!– this template handle text nodes –>
<xsl:template match=”text()”>
<xsl:variable name=”t” select=”.” />
<xsl:choose>
<!– test to see if it is a number –>
<xsl:when test=”string(number($t)) != ‘NaN'”>
<xsl:value-of select=”$t”/>
</xsl:when>
<!– deal with any case booleans –>
<xsl:when test=”translate($t, ‘TRUE’, ‘true’) = ‘true'”>
<xsl:text>true</xsl:text>
</xsl:when>
<xsl:when test=”translate($t, ‘FALSE’, ‘false’) = ‘false'”>
<xsl:text>false</xsl:text>
</xsl:when>
<!– must be text –>
<xsl:otherwise>
<xsl:text>&quot;</xsl:text>
<xsl:value-of select=”$t”/>
<xsl:text>&quot;</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:if test=”position() != last()”>
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:template>

<!– this template handles attribute nodes –>
<xsl:template match=”@*”>
<!– attach prefix to attribute names –>
<xsl:text>&quot;</xsl:text>
<xsl:value-of select=”name()”/>
<xsl:text>&quot; : </xsl:text>
<xsl:variable name=”t” select=”.” />
<xsl:choose>
<xsl:when test=”string(number($t)) != ‘NaN'”>
<xsl:value-of select=”$t”/>
</xsl:when>
<xsl:when test=”translate($t, ‘TRUE’, ‘true’) = ‘true'”>
<xsl:text>true</xsl:text>
</xsl:when>
<xsl:when test=”translate($t, ‘FALSE’, ‘false’) = ‘false'”>
<xsl:text>false</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>&quot;</xsl:text>
<xsl:value-of select=”$t”/>
<xsl:text>&quot;</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:if test=”position() != last()”>
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:template>

</xsl:stylesheet></Xsl>
</WebPartPages:XsltListViewWebPart>

The Image below shows a sample JSON object called MAP_Contracts that was generated using this XSLT. Note that the generated object has an array called rows that contains field called values that contains the data. In this example I have grouped the view on Exchange so the fields Exchange.COUNT.group, Exchange.groupindex, Exchange.newGroup and exchange.urlencoded were added by the webpart and could be used to do grouping on the client side.

jaonvariable

Posted in ajax, javascript, sharepoint | Leave a comment

Automatically retry jquery ajax calls to listdata.svc after HTTP 500 Errors

I had an issue with a SharePoint page that was making  jQuery ajax calls to get the valid values of a Choice Column on a list. It worked fine  usually but once in a while the ajax calls would return an HTTP 500 Internal Server Error, and the user would see this:

error500

This was usually first thing in the morning, so I assume it happens when SharePoint is warming up.  The ULS Logs gave me no clues.   If the user refreshed the page, the error disappeared.

I found a nice jQuery plugin that will automatically retry  an ajax request after an error  here: https://github.com/johnkpaul/jquery-ajax-retry.

After adding that plugin, I just needed to change one line of code:

return $.ajax(ajaxOptions);

to

return $.ajax(ajaxOptions).retry({ times: 3, statusCodes: [500] });

and that fixed the problem.

Posted in ajax, javascript, jquery | Tagged , | 1 Comment