CHUVASH.eu

CHunky Universe of Vigourous Astonishing SHarepoint :)

Category Archives: sharepoint

Switch back to Modern UI

Just a little tip. When you switched to a Classic View of Site Contents, you can switch back by removing a cookie called “splnu”.

Tips and tricks for Site Collection App Catalogs

Site Collection App Catalogs (SCAC) are much appreciated, thank you, Office 365 Team. Here is a couple of tips and tricks for SCAC.

Tip #1 You don’t need Tenant Admin rights to add a new Site Collection App Catalog

I have seen many blogs, forum threads etc that state that only Global Tenant Administrators can add new Site Collection App Catalogs. The truth is that a SharePoint Admin rights are enough. The trick is to make that SharePoint Admin Account to a site collection administrator of the app catalog site. To be precise the account that adds a new SCAC must have Manage Web Permissions, as stated in error message:

Add-SPOSiteCollectionAppCatalog : Must have Manage Web Site permissions or be a tenant admin in order to add or remove sites from the site collection app catalog allow list

Tip #2 List all Site Collection App Catalogs

To list all the SCACs in your tenant navigate to that url:

<appcatalog>/Lists/SiteCollectionAppCatalogs/

The official MSDocs state (2019-08-26):

Currently, it’s not possible to list all site collections in the tenant that have the site collection app catalog enabled.

The fun fact was that I sherlocked it since I knew my account needed access to the main App Catalog site. So there must be some information that is stored. How is it done a là SharePoint – yes, it is stored in a hidden list. Like in the olden days. 🧐

Just because I can should I use Private Office 365 CDN

This is about a topic brought up by Waldek Mastykarz: Just because you can should you use the Office 365 CDN. In my post I want to take a closer look at the private CDN option in Office 365. Please note, the whole thing is subject to change, and it reflects the circumstances at the time of writing – 2019-08-26.

I’ll skip the introduction of Office 365, let’s jump directly to the Private CDN option. Consider following scenarios.

I am developing SPFx solutions, is the Private Office 365 CDN something for me?

No, you can’t use private CDN for SPFx, because SPFx cannot handle changing Urls from a Private CDN.

I use Modern Team Sites and Communication Sites. Is the Private Office 365 CDN something for me?

No, the urls are changing. You cannot “hardcode” them. Automatic URL Rewrite works only on classic Publishing Sites.

I have Provider Hosted Add-Ins. Is the Private Office 365 CDN something for me?

No, the referrer needs to be a subdomain of sharepoint.com.

Security

The whole point of having a private CDN is that it is not available for strangers. But when you enable it, you’ll see an eligible warning:

WARNING: This is a feature built on a 3rd party application with privacy and compliance standards that differ from the commitments outlined by the Microsoft Office365 Trust Center. Any data cached through this
service does not conform to the Microsoft Data Processing Terms (DPT) and is outside of the Microsoft Office365 Trust Center compliance boundaries.

The risk might be low, but if an attacker gets a url that looks like this: https://privatecdn.sharepointonline.com/takana16.sharepoint.com/sites/cdn/privatecdnorigin/africa_private.jpg?eat=1566833073_cc22be0bd8c3534b83bbf38cfa3aa013923baa670e0874303efaaae1e9a86da7&oat=1566833073_5951c0c968da9de384f63b556ad42915aa01 then it is just a matter of downloading within 30-90 minutes. That’s how long those tokens in the URL are valid.

Adding Referer: <subdomain>.sharepoint.com as a Request Header is needed to download a resource.

If you remove an asset from the private cdn origins, it takes up to 15 minutes for the link to be invalidated. Opposed to an immediate effect for a direct link to an asset in a document library.

To keep it more secure, the default private cdn origins should not be included, especially */SITEASSETS, Because site assets can have important information, and this makes every single site assets library vulnerable, asterisk means all.

Even the CDN Policies should be restrictive.

Overall, if the usage area is small, the performance gain is little, we should not enable it at all. Because: any cached data in a private Office 365 CDN is outside of the Microsoft Office365 Trust Center compliance boundaries.

Performance

I have tried the private CDN. My setup was a document library with three versions of a picture that was 2,4MB that I put to three different libraries:

  • privatecdnorigin/africa_private.jpg
  • publiccdnorigin/africa_public.jpg
  • nocdnorigin/africa_nocdn.jpg

On the publishing site I inserted three images on a page and compared the load time in the DevTools. During this test I had Cache Disabled. I got following results:

  • private, public, nocdn
  • 3.04s, 3.03s, 3.24s
  • 1.78s, 1.77s, 1.75s
  • 1.99s, 1,95s, 3.32s
  • 1.67s, 0.73s, 0.72s
  • 1.73s, 1.71s, 1.97s
  • 1.60s, 1.58s, 1.67s

So only once I got a bigger difference, otherwise it took the same time to load a picture from a document library without CDN.

To be fair, it is a very simple performance test. Tests with bigger files, different geographical locations would probably give a more detailed view of that. And still, without a URL Rewrite that is only present on Publishing sites, you cannot take use private cdn origins.

Conclusion

Private CDN in Office 365 can be interesting in future, but today, the usage is narrow (only publishing sites can refer to assets in a private CDN), the performance gain is little and lower security makes it to a bad choice.

My list of _layouts pages

There are many resources on the internet that list _layouts/15 urls in SharePoint. Some are outdated, some are too short, some are to long. Here is my list of the urls, that I am going to update when I need. All the urls start with [Your-Tenant].sharepoint.com/sites/[Your-Site]/_layouts/15/

Here we go:

  • viewlsts.aspx – Site Contents, Modern View
  • viewlsts.aspx?view=14 – Site Contents, Classic View
  • appinv.aspx – Grant Permissions to an App
  • appregnew.aspx – Register a new SharePoint Application
  • appprincipals.aspx – List Registered Add-Ins
  • CreateGroup.aspx – Create Site page (Team and Communication)
  • TA_AllAppPrincipals.aspx – List all app principals

Update Field.JSLink using JSOM or REST

Today I have just a little code snippet to share. This code snippet shows how to update the JSLink property for an existing field using JSOM and REST. For REST I use sharepoint-utilities.

var updateJsLinkCsom = function(config) {
	var ctx = SP.ClientContext.get_current();
	var web = ctx.get_web();
	var lists = web.get_lists();
	var list = lists.getByTitle(config.listTitle)
	var fields = list.get_fields();
	var field = fields.getByInternalNameOrTitle(config.fieldTitle)
	field.set_jsLink(config.jsLink)
	field.update()
	ctx.executeQueryAsync()
};
var updateJsLinkRest = function(config) {
	SP.SOD.registerSod('sputils.js', '<your cdn endpoint>/sputils.min.js')
	SP.SOD.executeFunc('sputils.js', '', function() {
		var url = _spPageContextInfo.webAbsoluteUrl
			+ '/_api/web/lists/getbytitle(\''
			+ config.listTitle + '\')/fields/getbyinternalnameortitle(\''+ config.fieldTitle + '\')';
		var payload = {'__metadata': {'type': 'SP.Field'}, 'JSLink': config.jsLink};
	    var config = {'headers' : {'X-HTTP-Method': 'MERGE' }};
		sputils.rest.post(url, payload, config);
	});
};
var config = {
	listTitle: '<your list title>',
	fieldTitle: '<your field title>',
	jsLink: '~site/<your jslink>'
};
updateJsLinkCsom(config);
updateJsLinkRest(config);

A couple of notes, to update a field we need:

  • A POST request
  • with a header ‘X-HTTP-Method’: ‘MERGE’
  • with __metadata: { type: SP.Field } and JSLink property in the payload
  • and X-RequestDigest‘ header (but it is nicely handled by sharepoint-utilities)

Trigger SP2010 Workflows using JSOM

Today I found out how to start workflows in JSOM (JavaScript Object Model in SharePoint). Nothing special, but since it is not documented, it took me a while to find a solution. Here is the code which I want to keep as simple as possible.

What you need to start a SP2010 Workflow for a list item or a document in JSOM, you need to load SP.WorkflowServices.js and you need to create the manager and get the service, then you can trigger a workflow using the workflow name, the list guid and the guid of the list item:

var ctx = SP.ClientContext.get_current();
var workflowServicesManager =
	SP.WorkflowServices.WorkflowServicesManager.newObject(ctx,
		ctx.get_web());
var service = workflowServicesManager.getWorkflowInteropService();
service.startWorkflow(workflowName, null,
	listGuid, plainItem.guid, initiationParams);

Here is the code to trigger a workflow for multiple items:

//fire the workflows
function fire2010WorkflowForListItems(ctx, listGuid, plainItems) {
	var workflowServicesManager =
		SP.WorkflowServices.WorkflowServicesManager.newObject(ctx,
			ctx.get_web());
	var service = workflowServicesManager.getWorkflowInteropService();
	for(var i = 0; i &lt; plainItems.length; i++) {
		var plainItem = plainItems[i];
		console.log('scheduling workflow for id: ', plainItem.id);
		service.startWorkflow(options.workflowName, null,
			listGuid, plainItem.guid, options.initiationParams);
	}
	console.log('now executing...');
	ctx.executeQueryAsync(function() {
		console.info('yes, workflows completed for '
			+ items.length + ' items');
	}, function() {
		console.error('it didnt go well');
	});
}

 

The code above is inspired from this gist and sharepoint stackexchange. It is a simplified version that only works for list item workflows and SharePoint 2010 workflows.
Here is an example how you can get multiple items and batch start a workflow:

//just a couple of variables
var options = {
	workflowName: 'Behörigheter',
	listName: 'Documents',
	initiationParams: {}
};
//load list items
function startWorfklows() {
	//Start 2010 Workflow for a List Item
	var ctx = SP.ClientContext.get_current();
	var web = ctx.get_web();
	var lists = web.get_lists();
	var list = lists.getByTitle(options.listName);
	ctx.load(list);
	var items = list.getItems(new SP.CamlQuery());
	ctx.load(items);
	ctx.executeQueryAsync(function() {
		var listGuid = list.get_id() + '';
		var en = items.getEnumerator();
		var plainItems = [];
		while (en.moveNext()) {
			var it = en.get_current();
			//do not take checked out files, it won't work
			if (!it.get_item('CheckoutUser')) {
				plainItems.push({id: it.get_id(), guid: it.get_item('GUID') + '' });
			}
		}
		fire2010WorkflowForListItems(ctx, listGuid, plainItems);
	}, function() {
		alert('boom');
	});
}

//Load Worfklow Js dependency
var wfScript = 'SP.WorkflowServices.js'
SP.SOD.registerSod(wfScript,
	_spPageContextInfo.webAbsoluteUrl + '/_layouts/15/SP.WorkflowServices.js');
SP.SOD.executeFunc(wfScript, '', startWorfklows);

Why I needed it

I created a simple workflow that is triggered on Item Added and Item Updated. Unfortunately there are already thousands of items in the document library. To trigger them manually is nothing good. But a simple javascript solution did exactly what I wanted.

Minimal Download Strategy. Simple

There are many correct ways (1234, 5…) of making scripts work with the Minimal Download Strategy Feature (MDS) in SharePoint 2013 and 2016. But to be honest – every time I need it, I get confused. So now it is time to find a simple solution for that.

Who is better at it than the developers of the SharePoint themselves? Look at the MDS code in the built-in Display Templates:

mds-001

Let’s keep it as simple as Item_Default.js, let’s take it as it is and create our own scripts. Here is a skeletton of and MDS-ready script:

function runMyCode() {
	var time = new Date().toISOString();
	console.log('runMyCode', time );
}
runMyCode();
if (typeof(RegisterModuleInit) == 'function') {
	var scriptUrl = '/Style Library/runMyCode.js';
  RegisterModuleInit(scriptUrl, runMyCode);
}

Which boils down to this in pseudocode:

  1. Execute your code
  2. Determine if MDS is enabled
  3. If MDS is enabled
  4.       Register your code for execution

That’s it. No more overcomplicating.

Related findings

External scripts (outside SharePoint Site Collection, from CDN) within ScriptBlock of a custom action (like AddJSLink in PnP)  work withou any MDS-adjustments. The ScriptBlock adds a script tag to the head of the html document, and it the js reference is added to every AjaxDelta load. I would need more time to find out why. The good news is: it is an argument for using more CDN solutions in SharePoint.

Old stuff

A while ago I wrote two blog posts about MDS. Now I realise they were overcomplicated and the solution required customizations of the master page and 3rd-party scripts.

  1. Make your javascript code work with MDS part 1
  2. Make your javascript code work with MDS part 2

 

 

Minimal Display Template

We want to use our own Display Templates on Non-publishing sites – our team sites. Without the Publishing Feature activated you have to create an own javascript file. Here is short and concise instructions how to install it: Display Templates on Non-publishing Sites.

As described on that blog, you can make copy of an existing Item_Default.js and adjust to your needs.

I also asked Elio Struyf and I got the same tip. I did create my starter template. Here I want to share this very minimal javascript based Display Template. The real Minimal Display Template is in the SPCSR github repository: Item_Minimal.js It has been improved by Elio Stuyf himself 🙂

(function () {
  // Config contains variables that are defined in one place
  var config = {
    propertyMappings: { 'Path':null, 'Title':['Title'] }
  };
  var templateUrl;

  var register = function () {
      if ("undefined" !== typeof (Srch) && "undefined" !== typeof (Srch.U) && typeof (Srch.U.registerRenderTemplateByName) === "function") {
              Srch.U.registerRenderTemplateByName(templateUrl, render);
          }
    };
    render = function (ctx) {
      // Display template data
      var cachePreviousTemplateData = ctx.DisplayTemplateData;
        ctx.DisplayTemplateData = {
        'TemplateUrl': templateUrl,
        'TemplateType': 'Item',
        'TargetControlType': ['SearchResults', 'Content Web Parts'],
        'ManagedPropertyMapping': config.propertyMappings
        };
        var cachePreviousItemValuesFunction = ctx.ItemValues;
        ctx.ItemValues = function(slotOrPropName) {
                return Srch.ValueInfo.getCachedCtxItemValue(ctx, slotOrPropName);
        };

      // Retrieve managed property data
      var path = $getItemValue(ctx, 'Path');
      var title = $getItemValue(ctx, 'Title');

      // HTML markup for an item
      var htmlMarkup = String.format( '<div>' +
                        '<a href="{0}" title="{1}">{1}</a>' +
                                        '</div>', path, title);

        // Caching
        ctx.ItemValues = cachePreviousItemValuesFunction;
        ctx.DisplayTemplateData = cachePreviousTemplateData;

        // Return the HTML markup
        return htmlMarkup;
    };

  // Retrieve all the loaded scripts
  var allScripts = document.getElementsByTagName("script");
  // Get the last script file (this is the current DT file)
    var scriptUrl = allScripts[allScripts.length - 1].src;
    if (scriptUrl.indexOf('/_catalogs/') > 0) {
      // Remove the query string 
      if (scriptUrl.indexOf('?') > 0) {
        scriptUrl = scriptUrl.split("?")[0];
      }
      // Insert the site collection token
      templateUrl = '~sitecollection' + scriptUrl.substr(scriptUrl.indexOf('/_catalogs/'))
    // Register the template to load
    register();
      if (typeof (RegisterModuleInit) === "function" && typeof(Srch.U.replaceUrlTokens) === "function") {
          RegisterModuleInit(Srch.U.replaceUrlTokens(templateUrl), register);
      }
  }
})();

Benefits of js approach

If you choose to directly upload and mantain a javascript file you’ll get following benefits:

  • You can use the same display templates on Non-publishing sites and they work directly in SharePoint Foundation
  • You’ll get intellisense
  • You can run static code analysis agains your javascript code and you can create unit tests
  • It is more readable understandable from developer to developer
  • It is easier to follow best practices for javascript
  • It encourages reusing javascript components for Display Templates, CSR (JSLink) Templates.
  • You like TypeScript? Well, then it is best to skip the “grey” code in html comments.

Findings

  • You cannot use ‘use strict’ in your js file.

How to create a Display Template

Rename the Item_Minimal.js file to your name, update some properties.

Upload it to _catalogs/masterpage/Display Templates/Search/<your-folder>

Use ContentType: Display Template Code

displaytemplate-002

Template Level: Item and Managed Property Mappingsdisplaytemplate-003

Create a new Result Type. Every time you update the Display Template (item properties) you have to update the Result Type.

displaytemplate-001

Moving from Minimal to Better

Of course the minimal display template is not enough. There is a whole lot of things we can do, I prepared some tips (my own “best practices”):

  1. Learn the Search Display Template Syntax: Corey Roth – Useful js for Display Templates
  2. Follow Elio Struyf’s 10 tips for working with Display Templates (except the first one – javascript is better 🙂 )
  3. Prefer JavaScript Display Templates if you are a developer and you want to  have a better control
  4. Never update the built-in Display Templates.
  5. Centralize reused parts of Display Templates – utilities, common parts, css. Use a CDN site for that.
  6. Check out the SPCSR github repo, there are many Display Templates that you can use or be inspired from.
  7. Create your own folder in _catalogs/masterpage/Display Template/Search to have all your Display Templates within a site collection.

 

SharePoint Utilities – a promising JavaScript Framework

My colleagues at Bool have developed a new JavaScript framework for SharePoint – sharepoint utilities. It started on our DevDay last year – a whole free day when we could learn new things, try out new techniques or build something that was not even requested from a customer. I was not working on sharepoint utilities, so I almost forgot it until…

I recently  re-discovered sharepoint utilities. It is on Github, it is MIT licensed and contributions are welcome. The core of sharepoint utilities (sputils) is a set of wrappers for Search, TermStore, REST that allow you be more productive as a developer.

What I find especially compelling with that it contains some other fundamental stuff that every SharePoint developer needs:

  • XHR, you only need to use sputils.rest.get or sputils.rest.post to make Ajax requests to SharePoint, no need for jQuery or sprequestexecutor.js
  • Promise, it contains a minimalist Promise framework – no need for jQuery.Deferred. All calls to SharePoint _api are wrapped in promises. So there is a better way of making calls to SharePoint.
  • DateTime, it contains some useful functions for working with dates and time.
  • Functional JavaScript. No need for underscore.js or lodash.js.

To me, XHR and Promises are two important parts that I need in every solution. So instead of linking jQuery and preparing reusable XHR functions for SharePoint, I’d recommend using sharepoint-utilities.

A tiny tool for User Custom Actions

hehyuaf

Everybody loves User Custom Actions in SharePoint. That’s the only recommended way of customizing SharePoint. You have heard about it. Unfortunately there is no convinient way of administering them. People have their console applications or powershell scripts to add, update and delete user custom actions. It works but it is hard to open up Visual Studio or PowerShell every time you will try out an idea on a test site.

To overcome this, I have created a tiny little tool, packaged as a bookmarklet for your browser. When you click on it, it will show your existing user custom actions and you can add new user custom actions.

It is an ongoing little project, available on github, contributions are welcome. What’s left is:

  • Implement Delete operation
  • Implement Update operation
  • Improve the UI.

Here is how it looks today:

usercustomaction-001

To use this tool, add a new bookmark in your browser and copy the content of bookmarklet.html as the url.

Resources on that topic:

Creating a bookmarklet for Exporting Web Parts

REST API for User Custom Actions (MSDN)

What it can be used for

It can be used on test sites to try out ideas. But do not use it in Production. On ther other hand, parts of this script can be used in other scenarios, like adding new functionality by site owners directly from the browser, where data is predefined.

How to add a JavaScript code

You can use this boilerplate code to add a UserCustomAction, just put it into the textarea and click “Add new”:


var fileref=document.createElement("script");
fileref.setAttribute.src= "YOUR-SCRIPT";
document.getElementsByTagName("head")[0].appendChild(fileref);

Best practices for User Custom Actions

  • Add a ScriptBlock instead of ScriptSrc. In that way you can reference javascript and css files outside you site collection – ultimately from a CDN site.
Вула Чăвашла

VulaCV - Чăвашла вулаттаракан сайт

Discovering SharePoint

And going crazy doing it

Bram de Jager - Architect, Speaker, Author

Microsoft 365, SharePoint and Azure

SharePoint Dragons

Nikander & Margriet on SharePoint

Cameron Dwyer

Office 365, SharePoint, Azure, OnePlace Solutions & Life's Other Little Wonders

paul.tavares

Me and My doings!

Share SharePoint Points !

By Mohit Vashishtha

Jimmy Janlén "Den Scrummande Konsulten"

Erfarenheter, synpunkter och raljerande om Scrum från Jimmy Janlén

Aryan Nava

DevOps, Cloud and Blockchain Consultant

SPJoel

SharePoint for everyone

SharePointRyan

Ryan Dennis is a SharePoint Solution Architect with a passion for SharePoint and PowerShell

SharePoint 2020

The Vision for a Future of Clarity

Aharoni in Unicode

Treacle tarts for great justice

... And All That JS

JavaScript, Web Apps and SharePoint

blksthl

Mostly what I know about SharePoint - CommunicoCuspis

SharePointDiver

SharePoint på ren svenska