Clearly we’re in a transitional period in Office 365 at the time of writing (June 2017), where modern SharePoint experiences are available - modern sites and pages for example - but not everything is fully joined-up yet. That said, it’s a fast-moving landscape and part of the consultant’s role is to keep up with the best way to deliver solutions. For once, I hope this is a blog post which dates very quickly – certainly I have had to add bits of info as I’ve been writing it, and I’ll come back again and update the table below as things get announced/released.
Going back to that ‘transitional period’ - this is especially the case for organizations with collaboration workloads, where there’s a need to create some kind of site template for team sites. This is still very common for our clients, even if it’s just a need to provide a different home page experience or add some lists/libraries/content types/global admins to the site. After all, I think that *whatever* Microsoft provide as the default experience, many orgs benefit from some lightweight changes to this – and so site templating continues to be important in SharePoint. I hope Microsoft don’t lose sight of this. Certainly when I consider my wish list, many items relate to “doing team sites at scale” in SharePoint – so perhaps let’s think about that first.
Current site templating challenges
Currently site templating is challenging because:
Want to use Group sites? Well, it’s challenging to template these currently, because:
Can’t currently specify a custom template
Can’t currently be notified that a new site has been created, because there are no web hooks
[By the way, I agree there are ways around this (e.g. web job/function which polls for new sites), but none are pretty because users may start using the site in one state, only for it to change as they are working in it..]
Want to use non-Group SharePoint sites? Currently challenging to template these *and get modern experiences*, because:
Even the Patterns and Practices (PnP) site templating doesn’t currently allow provisioning of modern pages – at least, not without some dev effort to extend it
For our clients who don’t want to use Groups, that 2nd approach is becoming common for us. But it would be nice if it was more baked-in/required less work. It’s coming I know (see info in my table below), but there’s no harm in nudging people along the way ;)
My current wish list
Here’s an extract from a PowerPoint slide I recently to discuss my current list of “asks” to Microsoft:
Let me expand on those in a bit more detail:
Thing
Status (June 2017)
Notes
Global deployment of SPFx web parts
Needed so that SPFx app does not need to be installed to each site
Ideally at different scopes e.g:
All sites of a certain type e.g. all group sites, all regular team sites etc.
See slide at the end of this article. Initial solution will allow web parts to be available across sites, but without much control (e.g. my “scopes” examples).
Expand PnP schema to include provisioning modern pages and web parts
Needed to allow provisioning of team sites *with modern page as home page* – without code/PnP extensibility provider)
Expected in next release of PnP Core (August 2017 release). It’s in the XML schema already...
Remove restrictions on “no script” sites (esp. property bag)
Needed to bring more extensibility to Group sites (or other sites with “no script” enabled
Writing to property bag is common in many customization scenarios
Done!
It’s now possible to disable “no script” on modern team sites, including Group sites. The Customizing modern team sites page has been updated to reflect this (June 26 2017)
Missing controls and tagging support
Taxonomy control
Person/Group control
Calendar control (for non-Group sites)
Full Content Search web part (or similar)
No specific news
Modern pages in team sites are great for editing and display, but currently not so good when it comes to some types of content and tagging. Missing controls include the ones I’ve listed (difficult to have a “Page contact” or “Site owner” for example), but the tagging/metadata support is lacking too due to the (lack of) fields on the Site Page content type. Rolling up these pages with a filter is tricky in the current arrangement.
More known issues with flexibility of these pages – the single column aspect in particular. But, this is relatively low-hanging fruit for Microsoft and we can expect these resolved soon I think.
Expand SPFx extensions model (bonus item)
No specific news
Additional areas to target, not just “PageHeader” and “PageFooter”. The product group have said more areas are coming for modern pages in team sites (and presumably communication sites too).
In terms of the first item, global deployment of SPFx web parts, Vesa recently used this slide to discuss what’s coming:
That sounds like a reasonable plan, since at least the long-term solution would give us full control. The short-term solution would clearly mean that all SPFx web parts are available everywhere (e.g. in my team sites, my intranet and my communication sites) which might not make sense, so hopefully the long-term arrangement isn’t too far away.
Communication sites
Communication sites are starting to launch and Microsoft’s “Ask Me Anything” session was yesterday (28 June 2017). I was disappointed not to hear any detail about templating of communication sites, so that would certainly be another wish list item. I know there are “new” templating options for those coming, but I still want the ability to use PnP site provisioning and XML too – after all, we still need the control and ability to specify all the aspects of a site template that PnP provides, so why use another approach? Also, having a mismatch between what I’m doing everywhere else and for comms sites would be sub-optimal too. Hopefully PnP provisioning will be possible there too – I really hope so!
What else?
I could certainly think of a few more items. But what did I miss that’s on your list at the moment?
UPDATE 26 JULY 2017 – Wait, how did I miss the lack of API for Microsoft Teams? Again, I know it’s coming, but I’m really looking forward to the ability to create a template for a team with appropriate tabs and connectors, and the ability to create Teams programmatically (perhaps alongside an Office 365 Group/SharePoint site). Hopefully not too long to wait!
New tools for customizing modern SharePoint sites and pages in Office 365 have arrived (in preview at the time of writing, June 2017). These are known as “SharePoint Framework (SPFx) extensions”, and replace some tools that SharePoint developers have long used to deliver key scenarios such as:
Adding JavaScript to every page in a site/web
Injecting some content (e.g. a mega-menu/global navigation or message bar) into every page
Popping up dialog boxes in an integrated way
Adding items into certain toolbars/menus in SharePoint
Changing the rendering/behavior of a specific field in a list
In other words, SPFx extensions provide the equivalent of CustomActions and JSLink – previous dev approaches which didn’t necessarily translate to modern pages.
In this article I want to focus on the first two scenarios listed above (in bold) – referencing some JS on every page, and also running some code to put something in the header area of the page. The documentation provided by Microsoft does a good job on the 2nd scenario, but sometimes it’s good to have something a bit more visual so I’ll provide more screenshots. I’ll also talk about the scenario where you don’t necessarily want to add some *content* to the page, but you do want to add *some other form of script* to run on every page (e.g. analytics/whatever).
In terms of injecting content into the page, we now have the following zones in modern pages (N.B. these are the names from SPFx 1.2 onwards):
Top
Bottom
DialogContainer
N.B. We can expect more zones in the future! Here’s what the Top (header) and Bottom (footer) zones look like:
Key information
Microsoft are currently saying that SPFx extensions will hit General Availability (i.e. fully-released in all tenants and suitable for production use) in fall/autumn of 2017. Until this time they are in preview.
Also be aware that what makes the new extensions possible is Microsoft's updates to tenants (only in developer tenants at the time of writing, not even in First Release), and updates to the Yeoman Generator that developers use to get started - this has a new set of component types which get you started with the right default code.
SPFX 1.2 changes
Changes to placeholder names – “Top” and “Bottom” insteaad of “PageHeader” and “PageFooter”
The onRender() method is deprecated/should no longer be used in SPFx extensions
Previous limitations with modern pages
Modern pages have been frustrating because:
No possibility to run custom script
Global JS added with previous methods (CustomAction + JSLink) did not run here – only on “classic” pages
Corresponding lack of page extensibility
No way to inject content into the page
What’s changing here is that Microsoft are providing a hook to run your code, and are also providing named placeholders on modern pages – zones of the page which you can add content to. So long as you stick to these zones and don’t arbitrarily “hack” the page by changing other DOM elements (e.g. with jQuery or similar), then Microsoft effectively guarantee that updates to Office 365 will not impact your customizations.
The script you provide has to be installed to an app catalog and deployed that way, meaning that there is effectively an approval step. This means that simply editing the page to add a Script Editor web part no longer exists as the easy option – the script must be OK’d by an administrator. Lots of debate on this one of course, but ultimately it’s what Microsoft need to do to facilitate more governance and safeguard Office 365 as a stable platform.
Targeting placeholders such as the Top and Bottom zones
In earlier versions of SPFx, some pages only had the Top zone but missed the Bottom zone. That’s now been fixed and it seems that if the Top zone exists on a page type (e.g. modern page, Site Contents page, document library or list page etc.), the Bottom one will too:
I showed a relatively narrow bar above, but there’s nothing to stop you making that top zone larger if you want to with CSS (this image is zoomed out):
But of course, all this only applies to modern pages – classic pages do NOT have these zones or support SPFx extensions in general:
I’ll talk about the end-to-end process later, but to get straight to the code - with some minor tweaks/simplification to the suggested code in the documentation, mine looks like this:
And the CSS is implemented by adding an SCSS file in your extension’s directory – mine is named AppCustomizer.module.scss and has the following content:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Remember this is imported to the class for your customizer e.g:
import styles from './AppCustomizer.module.scss';
So, the key elements here are:
A class that derives from the ApplicationBaseCustomizer class
Use of the this.context.placeholderProvider.tryCreateContent() method to get a reference to the appropriate placeholder and it’s content - and the fact that it gives you the DOM element to manipulate (e.g. set innerHTML)
Deployment options – global or site-by-site
In terms of what associates your customizer to the site, there are two ways of doing this in production:
Site-by-site – in this approach, you add some declarative XML to your app packaging, and then ensure the app is installed from the App Catalog to each site where your extension should operate. Specifically, your customizer has a manifest file which contains it’s ID ([MyCustomizer].manifest.json), and on top of this you actually add an elements.xml file with a CustomAction element (just like the old days!). This has a new "’ClientSideComponentId” attribute, and this must point to the ID of your customizer.
Global/scripted – in this approach, you set the skipFeatureDeployment attribute to “true” in youre package-solution.json file, and then use CSOM or REST to add a CustomAction programmatically to each web as you need (i.e. by iterating, or including into some provisioning code). See https://dev.office.com/sharepoint/docs/spfx/tenant-scoped-deployment for more details. When using this approach, the admin has the option of making the SPFx web part/extension globally available when installing to the App Catalog:
SPFx web parts will show up in every site, but as I say, for SPFx extensions you also need to take care of the programmatic association/registration to each site/web you require, using CustomAction/ClientSideComponentId. See my post Manage tenant-scoped SPFx extensions across your SharePoint sites for some PowerShell/C# code to do this.
But before packaging for production, there’s a mode when you can dev/test your customizer before worrying about packaging. This works by running a “gulp serve” locally and adding some querystring parameters to a modern page so that the manifest is loaded from localhost – it’s a bit like the “local SPFx workbench” equivalent but for SPFx extensions/customizers.
But I don’t need placeholders – I just want to reference some JavaScript on every page!
In this case, the code is somewhat simpler. If you have an external JS file you want to reference in a quick and dirty way, you could do this by dynamically adding a script tag to the <head> element of the page. My testing shows it seems safe to do this in the onInit method, but the onRender method would be fine also – in any case, it’s just the old-fashioned method like this:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
If the JS is hosted on another domain, you may need to enable CORS there (depending on what your JS is doing)
If you're referencing a module script, you could do this in a cleaner way by referencing it as an external module in the "externals" section of your config.json file (see Add an external library to your SharePoint client-side web part for more). I've tested and this approach does work with an Application Customizer
You could also choose to bundle your script if that made sense, and ensure it was referenced in the onRender method for your customizer. That should work too..
Process
The process is effectively the same whether you're targeting page placeholders or just referencing script on every page:
Update the SPFx Yeoman Generator if needed
The first step you might need to do is to update your SPFx Yeoman Generator – assuming you already have all the bits installed, you can do this by typing “yo” at the command-line and then going through the update process:
Once you’re ready to actually create your app customizer, do this by running that generator:
Give your solution a name, and ensure you select the “Extension” option:
In this case, we’re using Application Customizer (rather than ListView Command Set Customizer [CustomAction/toolbar replacement] or Field Customizer [JSLink/field replacement]):
Provide a name for your customizer and then a description:
The generator will then get busy creating your application with the appropriate files, and then you’ll see:
Your application has now been created and you’ll get the boilerplate code (which may look a little different to this in later versions of SPFx):
It’s a good idea to test running this in debug mode before making any code changes, so do this by running a gulp serve with the “nobrowser” switch:
The next step is to browse to a modern page, but adding some querystring parameters in the URL so that our *local* manifest for the customizer is loaded. First, open a browser to a modern page – a document library is a good choice:
And then in Notepad or similar, build the querystring parameters you need. This basic format of this is:
?loadSPFX=true&
debugManifestsFile=https://localhost:4321/temp/manifests.js&
customActions={"badba93c-7f98-4a68-b5ed-c87ea51a3145":{"location":"ClientSideExtension.ApplicationCustomizer","properties":{"testMessage":"Hello as property!"}}}
However, you’ll need to replace the ID with the one from your customizer’s manifest file:
If you paste that onto the end of the URL to the document library in your browser window and hit enter, you should see a warning message related to debug mode:
Click the “Load debug scripts” button, and then your code should execute and you should see the results – in the case of the boilerplate code, it’s an alert box:
Success! You’ve now run an Application Customizer in debug mode.
Packaging for production (site-by-site/declarative approach)
For this, I recommend following the steps in the documentation (start at Deploy your extension to SharePoint) – but below is an extract of the main steps. Ultimately it revolves around:
Building your app, and deploying the bundled JS files to somewhere like a CDN (just like an SPFx web part)
Adding some packaging files to your app, so that your customizer is called when the app is added to a site (a bit like feature activation – in fact, it IS feature activation ;))
Deploying the app package to an App Catalog, and then adding the app to a site
In terms of the process, key steps are:
Create SharePoint/Assets folder and add an elements.xml file:
Add the contents to elements.xml – set the “ClientSideComponentId” to identifier of your customizer i.e. the one found in the [MyCustomizer].manifest.json file (remember, you can skip this if you plan to use skipFeatureDeployment=true and globally deploy via script):
Edit the config\package-solution.json file – add a “Features” node to reference your elements.xml file. It needs contents similar to the following:
"features": [{
"title": "COB AppCustomizer - global JS",
"description": "Adds some JavaScript to every page in the site",
"id": "456da147-ced2-3036-b564-8dad5c1c2e34",
"version": "1.0.0.0",
"assets": {
"elementManifests": [
"elements.xml"
]
}
}]
Take care of some other steps related to CDN-hosting of your JS bundle (e.g. updating the ‘cdnBasePath’ property in the ‘write-manifests.json’ file), and then bundle and package your app using 'gulp bundle --ship' and 'gulp package-solution --ship' respectively.
As I say, head to the documentation for the full steps when you actually come to do this.
The app is then upload to the app catalog:
Notice that at this point, the admin needs to trust the application and will see where the remote files are hosted - in my case, I used the Office 365 public CDN:
You should then see your customizer take effect, and if you go looking you’ll see a web-scoped feauture (by default) which is binding your customizer to the site:
Other matters
Property bag – as shown in the “Build your first extension” page, there’s a property bag of sorts that can be used with customizers. In production mode, properties are specified in the CustomAction element in your elements.xml file. In my example, I chose to use values specified directly in the code, but this property bag provides some level of separation (but it is still burnt into your package)
I'm a technology leader with over 20 years' experience of working with Microsoft technologies to transform how organisations work, with the last decade in leadership roles. I'm the Products & Services Director at Advania UK and while more strategic these days, I have a background as a deep hands-on technologist - managing development teams, leading Microsoft 365 and SharePoint delivery, and writing code. I am one of a small number of Microsoft 365 ‘Most Valuable Professionals’ (for the last 16 consecutive years in my case) and regularly speak at Microsoft and industry events.
In technology terms, my core strengths are around the Microsoft cloud stack – especially Microsoft 365, AI technologies such as Azure OpenAI, Syntex/SharePoint Premium, Microsoft Viva etc.) and Azure.