Thursday, June 14, 2018

Scoped Bootstrap for SharePoint

I've got a project for a client that involves refactoring a lot of bad choices related to SharePoint branding in Classic mode sites.   (While I'm enthusiastic about the Modern Sites and new techniques using SPFx extensions, most of our customers are slow on the uptake).

I've had to do this a few times and this blog explains it perfectly, but due to some formatting issues over there and a desire for just the steps as I need to update/repeat this, I've summarized here:

  1. Open up a node capable command prompt (I use cmd)
  2. Create a directory for your solution (e.g. C:\Repos\ScopedBootstrap)
  3. Run npm install bootstrap@3.3.7 (or whatever version you need to work with)
  4. Go into the node_modules\bootstrap\less directory 
  5. Open bootstrap.less in VS Code.
  6. Place  .YourUniqueClassName{  before all the @import statements and close the curly brace at the end of the file.
  7. Go back to the ..\bootstrap directory (in node_modules)
  8. Run npm install to install the dependencies
  9. Run grunt dist
  10. Go to the node_modules\bootstrap\dist\css directory in the project and grab the css file you need.   If you open it you should see YourUniqueClassName appended to each seletor.   
  11. Once you reference this file it allows you to enable bootstrap at a certain level of the DOM simply by including .YourUniqueClassName in any elements that need it.   You can do it at a parent node level to affect everything else in the tree below.

Thursday, March 15, 2018

Switching between on-prem and SharePoint Online versions of PnP Powershell Commands

I use the PnP Powershell commands very frequently, but mostly only for SP Online.   Lately I've been working on a SP2013 customization and wanted to use the PnP PowerShell commands for SP2013.

If you have multiple version of the commands installed then you'll need to select which one you want to use.

This thread explains that you need to clear your PSMODULEPATH environment variables but then you'll need to an import-module when you need to work with a particular version.  Follow this guidance first:

Doing that is fine if you don't mind retyping your module path in the import, but doing it by friendly name is probably easier

Here's a quick PS command that saves you from having to manually type in the installation path:

Get-Module SharePointPnPPowerShell2013 -ListAvailable | Select Path | % { Import-Module $_.Path }

Just swap the SharePointPnPPowerShell2013 (in red) for SharePointPnPPowerShell2016 or SharePointPnPPowerShellOnline based on your needs.

Also, if you have multiple versions of the same module, you can specify which version you want to load in the Import-Module command's -RequiredVersion parameter. E.g.:

Get-Module SharePointPnPPowerShell2013 -ListAvailable | Select Path | % { Import-Module $_.Path -RequiredVersion 2.24.1803 }

If you aren't sure what flavor/versions of the commands you have simply run the first command in the pipeline with a wildcard in the module name:

Get-Module SharePointPnPPowerShell* -ListAvailable

Wednesday, November 8, 2017

Client Side Override the Site Logo's link in an SharePoint Site

In SharePoint sites that have any kind of hierarchy of subwebs, it is frequently desirable to override the logo's link behavior to link back to the root of the site collection.   There's a bunch of guidance on how to do this by overriding the master page, but not much on how to do this client-side, to avoid MasterPage manipulation.   Here's a script that helps to do this:

//update site icon link, SP overrides any direct reassignment so you have to change the existing ID
//and then create a dummy element with the existing ID to avoid future JS errors.
//read old link id
var prevId = $("#DeltaSiteLogo > a").attr('id');
//change the id
$("#" + prevId).attr({
      'title': 'Employee Portal Home',
      'href':window.location.protocol + "//" +
$("#DeltaSiteLogo > a").after("<a id='" + prevId + "' href='#' style='display:none'>Dummy</a>");

You'll need jQuery to do this and I'd recommend putting this into a file and then injecting via a SiteCollection scope custom action.   Here's the simplest way to do that: