Here’s the scenario: we have a site called Clients where we track potential clients and store files we get from them during discussions. Once they become an actual client, we create a Team Site, connect it to Teams, and we move the files into that site.
For years, this has frustrated me no end because when I create the new Team Site, it can take until overnight for that site to be visible in the Copy to / Move to dialog.
As I am wont to do, I finally turned to Twitter for a solution (though I don’t think this is the first time!). with the #SPHelp hashtag.
How do I get a site to show up in the Move to or Copy to dialogs? If there's a trick other than waiting overnight after creating a new site, I haven't found it. #SPHelp#veryfrustratingpic.twitter.com/c0Zt6rnAR4
It was gratifying to get many responses (and many people expressing the same frustration). Most of the suggestions were things I’d tried before: upload some dummy files to the new site to generate “activity”, follow the new site, etc. None of these actions seemed to reliably make the site show up where I wanted it.
Francis Laurin (@pzkfwg) and Gregory Zelfond (@gregoryzelfond) responded with the winning answer. Turns out the answer has been staring me in the face – though I don’t think it’s all that obvious. If you notice, the list of destinations on the left of the Copy to / Mode to dialog screen is called Quick access.
The solutions is to navigate to the specific Document Library you want to copy/move to and pin that Document Library to Quick access. That option has been there for a long time, but I never connected it to this process in my mind.
This great video from Greg shows how this all works:
Unfortunately, that feels backward. Ideally, Microsoft will make the Copy to / Move to dialog smarter and let us just search for the site we want to use as the destination. I shouldn’t need to go to that destination before I copy/move the content.
At the very least, the dialog should have some way to know that pinning to Quick access is a “thing”, by offering some in process help or tip. I’m thinking that a little info icon next to the words Quick access which takes me to an article which explains what Quick access even is would be a huge step in the right direction. When I Bingle for the *right* search terms “SharePoint quick access”, for example, I only find information about Windows Quick access (which seems to be broken for me lately, but that’s another post).
I feel like I’m a reasonably smart guy with some familiarity with the platform. This shouldn’t have been so hard to figure out.
You might be surprised by the title of this post. Isn’t our goal to get everyone’s content *into* SharePoint? Usually that’s the case, for sure.
But many times, in the course of a migration from an older (usually on premises) version of SharePoint, we identify whole sites or branches of subsites that simply don’t have any real purpose anymore. In some cases, we migrate the content into SharePoint Online, maybe into a site called Archive or similar. But other times we just want to save the content somewhere *in case* someone needs it. Which approach you take may hinge on your organizational culture, what storage mechanisms you have available, and the relative importance of the content – among many other things.
I’ve worked on a PowerShell script for this recently and used it for several different clients. With it, I can point at the top level of a branch of sites – maybe something like /sites/HR or /departments/Finance, and the script will download the content from that site, and recursively from all its subsites, etc. into a network location of my choosing, say C:\ or Z:\Archive.
I’ve run this script with both SharePoint 2010 and SharePoint 2016 as the source version, so I expect it will work for you in most cases.
The script is pretty straightforward because I use ShareGate’s PowerShell module to do all the heavy lifting: yet another reason why ShareGate rocks! If you have ShareGate installed on your machine, you have their PowerShell module as well, even if you didn’t realize it.
Note the ShareGate module requires you to be running PowerShell 5.x, NOT the more current PowerShell 7.x.
The script comes in two pieces:
downloadSite.ps1 – The script I actually run, after setting three variables appropriately.
downloadSiteFunctions.psm1 – A module with functions I call above. If you want to do recursion, you’ll need functions of some sort, and using a separate module gives some flexibility for reuse.
The parameter settings I show in the downloadSite.ps1 below were what I wanted for a particular scenario. When you read through downloadSiteFunctions.psm1 below, you’ll see what the impact of those settings is.
Import-Module -Name ShareGate # Requires ShareGate to be installed on the machine
Import-Module "./PowerShell/downloadSiteFunctions.psm1" -Force
# Setup
$sourceSiteName = "Name for the downloaded folder"
$sourceSiteUrl = "https://FarmOrTenantName/siteName/"
# The downloads will end up in a folder here named $sourceSiteName
$destTop = "Z:\" # Be sure to include a trailing backslash
# Any list or library in this array will be excluded from the downloads
$exclusionLists = @(
"Content and Structure Reports",
"Master Page Gallery",
"Reusable Content",
"Style Library",
"Web Part Gallery",
"Workflow Tasks",
"Microfeed",
"Site Pages",
"Site Assets"
)
# Process root web
# Delete existing folder - we assume we want to start from scratch
Remove-Item `
-Path "$($destTop)$($sourceSiteName)" `
-Recurse `
-Force
# Create new top-level folder
$top = New-Item `
-Path "$($destTop)$($sourceSiteName)" `
-ItemType Directory -Force
# Export lists
Export-SympLists `
-ParentFolder "$($top.FullName)" `
-WebUrl $sourceSiteUrl `
-Versions $false `
-ExclusionLists $exclusionLists `
-KeepEmpty $false `
-KeepLists $true
# Process subwebs
Get-SympSubwebs `
-ParentFolder "$($top.FullName)" `
-WebUrl $sourceSiteUrl `
-Versions $false `
-ExclusionLists $exclusionLists `
-KeepEmpty $false `
-KeepLists $true
The downloadSiteFunctions.psm1 file might seem complicated, but it’s just two functions:
Export-SympLists – Exports all list/library contents from a web using ShareGate PowerShell functions. The ShareGate function Export-List is a workhorse. It exports ALL lists and libraries to folders. Even better, it creates an Excel file containing the inventory with all the metadata as well as the Document (libraries) or Attachments (lists).
Get-SympSubwebs – Gets the subwebs of any web and exports their list contents using ShareGate PowerShell functions. By calling itself as a last step, the function enables recursion. Essentially, it “walks” the subsite (web) topology from the current site (web) on down.
<#
.DESCRIPTION
Exports list contents from a web using ShareGate PowerShell functions
.EXAMPLE
Export-SympLists -ParentFolder $parentFolder -WebUrl $webUrl -Versions $versions
#>
function Export-SympLists {
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(
# Parent Folder
[string]
$ParentFolder,
# Web URL
[string]
$WebUrl,
# Versions - Should we download versions (or only the latest version) $true = all versions
[boolean]
$Versions = $false,
# ExclusionLists - Array of list names to *skip* in the download
[array]
$ExclusionLists = @(),
# KeepEmpty - Will create a folder for every library even if it is empty.
# Setting this to $false will delete the empty folders.
[boolean]
$KeepEmpty = $false,
# KeepLists - Will create a folder for every list even if it is empty.
# Setting this to $true will keep all lists, regardless of their number of items.
[boolean]
$KeepLists = $false
)
Begin {
Write-Host "Processing web $($WebUrl)"
}
Process {
# ShareGate's Connect-Site
$srcSite = Connect-Site $WebUrl
# ShareGate's Get-List
$srcLists = Get-List -Site $srcSite
# Filter out the exclusionList items, if any
foreach ($exclusionList in $ExclusionLists) {
$newLists = $srcLists | Where-Object { $_.Title -ne $exclusionList }
$srcLists = $newLists
}
# If there's something to download, do it.
if ($srcLists.length -gt 0) {
# If we want to keep versions
if ($Versions) {
$result = Export-List -List $srcLists -DestinationFolder "$($ParentFolder)"
}
else {
# Else we don't want to keep versions
$result = Export-List -List $srcLists -DestinationFolder "$($ParentFolder)" -NoVersionHistory
}
}
# If #KeepLists, then keep all lists
if ($KeepLists) {
$srcLists = $srcLists | Where-Object { $_.RootFolder -inotmatch "/Lists/" }
}
# If !$KeepEmpty, delete the folders which have no content
if (!$KeepEmpty) {
foreach ($list in $srcLists) {
$listPath = "$($ParentFolder)\$($list.Title)"
$documents = Get-Item -Path "$($listPath)\Documents\*" -ErrorAction Ignore
if ($documents.length -eq 0) {
Remove-Item -Path $listPath -Force -Confirm:$false -Recurse
}
}
}
}
End {
}
}
<#
.DESCRIPTION
Gets the subwebs of any web and exports their list contents using ShareGate PowerShell functions
.EXAMPLE
Get-SympSubwebs -ParentFolder $parentFolder -WebUrl $webUrl -Versions $versions
#>
function Get-SympSubwebs {
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(
# Parent Folder
[string]
$ParentFolder,
# Web URL
[string]
$WebUrl,
# Versions - Should we download versions (or only the latest version) $true = all versions
[boolean]
$Versions,
# ExclusionLists - Array of list names to skip in the download
[array]
$ExclusionLists,
# KeepEmpty - Will create a folder for every library even if it is empty.
# Setting this to $false will delete the empty folders.
[boolean]
$KeepEmpty = $false,
# KeepLists - Will create a folder for every list even if it is empty.
# Setting this to $true will keep all lists, regardless of their number of items.
[boolean]
$KeepLists = $false
)
Begin {
Write-Host "Getting subwebs of $($WebUrl)"
}
Process {
# ShareGate's Connect-Site
$siteConnection = Connect-Site $WebUrl
# ShareGate's Get-Subsite
$webs = Get-Subsite -Site $siteConnection
# Process each web
foreach ($web in $webs) {
# Remove illegal characters in the web title
$cleanTitle = $web.Title.Replace("#", "").Replace(":", " - ").Replace("/", "-").Replace("""", "'")
# Variable for the web's folder - note the leading "_"
$rootFolder = "$($ParentFolder)\_$($cleanTitle)"
# Create the web's folder
$newFolder = New-Item -Path $rootFolder -ItemType Directory -Force
# Download the lists/libraries with the provided parameters
Export-SympLists -ParentFolder $rootFolder -WebUrl "$($web.Address)" -Versions $Versions -ExclusionLists $ExclusionLists -KeepEmpty $KeepEmpty -KeepLists $KeepLists
# Get the web's subwebs - this is the recursion
Get-SympSubwebs -ParentFolder $rootFolder -WebUrl "$($web.Address)" -Versions $Versions -ExclusionLists $ExclusionLists -KeepEmpty $KeepEmpty -KeepLists $KeepLists
}
}
End {
}
}
Here’s an example. Let’s say I set the variables in downloadSite.ps1 like so:
# Setup
$sourceSiteName = "Brazil"
$sourceSiteUrl = "https://myTenant/Brazil/"
# The downloads will end up in a folder here named $sourceSiteName
$destTop = "Z:\"
I end up with something like the following set of folders. Brazil is the top-level site, and it has 3 subsites: Facilities, Human Resources, and Recruitment. Notice that each subsite’s folder’s name starts with an underscore so we can easily understand the tree at a glance.
Within each subsite’s folder, all the lists and libraries within it (if requested) are represented as folders.
Each library’s folder contains the Excel file with the metadata, and a subfolder containing the documents themselves.
Each list’s folder contains the Excel file with the metadata, and a subfolder containing the attachments, if any.
This script gives me a good scaffolding and some options I can use to get content out of SharePoint in an organized and broad way. I’ve already used it with two clients, and I firmly expect I’ll use it again.
Back in October, I was lucky enough to visit my friends at ShareGate, along with fellow MVPs Jasper Oosterveld (@jappie1981) and Maarten Eekels (@maarteneekels) We had several conversations with ShareGate’s Laurent St. Pierre (@laurent_sp), and those were recorded and released as a series of videos: The evolution of IT: Improving digital employee experience to boost productivity. The conversations have also led to ongoing asynchronous discussions about the things we said, for more elaboration and to expand on our thoughts.
Emily Mancini (@eemancini) and I had kicked around the topic and came up with the following list of ideas. Some made it into Rafael’s post, and others didn’t, so I figured I’d post our full list here.
Let us know what you think in the comments!
Clean up AD/AAD data – In most cases, targeting content to the right people is based on AAD data. This data determines which people belong in particular Microsoft 365 Group, especially if they are dynamic groups. If the organization can actually rely on that data being correct, they can build out much more personalized experiences. In most organizations, not only is the data wrong, but everyone knows it’s wrong and they can’t rely on it. No one even tries to get it fixed because they think it’s pointless. Spending time improving this data provides many benefits.
Focus on consultative customer service – If you’re like many IT departments, people don’t come to you for advice and assistance because they can’t get your time. Set up an informal (or formal) consulting capability for the organization. Let anyone in the organization get in touch during office hours to help them think through technical issues and starting solutions for themselves. This consultative focus can also become “market sensing”, in that it tells you what the organization is doing and what they need that you aren’t providing. Unless you have an amazing help desk, they probably don’t fulfill this role. Help desks tend to focus on solving immediate problems (which everyone wants them to do), and not longer-term efforts. If nothing else, it’s a different mindset.
Think about places you could solve problems – Imagine going out into the organization and saying something like “We know you struggle with people filling out so many of these forms. We’d like to help you automate and improve the process.” You’d be heroes! In other words, do external outreach in the organization: offering the very services you have capacity for – for free! You’ll be amazed at the goodwill this engenders.
Search analysis – Take a look at the searches people are doing and what happens. How many searches lead to useful results, and how many fail? A failed search is a content opportunity, and a successful search means that content matters and may need a review. That doesn’t mean that you in IT need to do that review, but you’d be providing great information to the content owners to make their content more valuable. You have the tools to do this in Microsoft 365, but many organizations simply don’t.
Run the assessment tool – The Microsoft 365 Assessment tool was created to help people move from classic to modern and from on prem to the cloud originally. It’s been expanded to help people understand how Microsoft Syntex might be a valuable toolset. But as part of the output, it gives you information about how to improve your information architecture. You need to be at least a SharePoint Admin to run the scanner, so running it for your Site Owners is a way to give them a gift to improve their content and its structure.
Recently, one of my clients pinged me because she wasn’t seeing any items in a list she knew had many items in it. She’s used the list many times in the past and she hadn’t changed any of the view settings.
I did all the usual stuff:
Checked permissions on the site: She was a Site Owner, as we both thought.
Looked for broken inheritance on permissions – unlikely to be the problem, since she was a Site Owner.
Restarting the browser
Rebooting the machine
A bunch of random stuff
I could see all the items in the list, as could a few others.
It seemed like such a simple thing, but nothing in my normal arsenal was solving the problem, so I turned to Bing.
There were some…interesting…solutions there, but the winner is simple:
Navigate to List Settings, then Advanced Settings
Scroll down to Offline Client Availability – Odds are it will be set to Yes, so change it to No and save.
Go back to the list and you should see the items again. If you’d like to (and I’d suggest you do), go back in and set Offline Client Availability to Yes again.
My theory is something happened to the caching for the list.
Guess what? The first post in the thread above was just about the same time offline mode rolled out for Microsoft Lists: Microsoft Lists gets ‘supercharged’ performance and offline support | Windows Central. If you didn’t know this ever happened, then it’s working exactly the way it’s supposed to work. If you lose Internet connectivity for a short time, you can continue working and the changes sync when you’re connected again. Heck, take your laptop to a desert island for a week and work on your list and your changes should sync when you plug in again.
When I’ve built solutions which rely on caching in the past, I’ve usually hidden a “cache buster” button somewhere in them. Caching sometimes goes sideways on you, no matter how good your code is. The “cache buster” button here was an advanced setting few people would even think of, much less know about. It would be great if there were some indication on the screen somewhere that maybe something is awry, but no such luck.
Leaving this here for anyone who might be as frustrated as I was, maybe even future me.
SharePoint lists & libraries: we all love ’em. With multiple views, they are like little apps.
[Why the heck do lists have a brand and logo but libraries don’t? And why aren’t the two just considered simply variations on a concept by Microsoft?]
Do you find yourself using both the modern & classic view settings? If so, why do you continue to use the classic view settings? I’m building a list (ha!) for the Microsoft folks. I’m after feedback about view settings in particular, but anything you find yourself returning to the classic settings UIs for regularly would be helpful.
For example: I go to the classic view settings to display the Title in libraries or to create a “folderless” view.
Here’s what I’ve collected so far. Please reply in the comments and let me know what we’re missing! I’ll keep adding to this post as I hear from more folks. You can help by spreading the word by amplifying my posts on Twitter, Mastodon, and/or Facebook. I’m consolidating and rewording for consistency – my apologies if I mangle what any of you said. Just let me know if I’ve missed anything!
List Settings
Manage Content Types (Order, Field Hidden)
Access to field to see internal name in the url parameters
Manage View Settings
Reindexing List “if you want to include them in the Search”
Versioning
Item permission “Create items and edit items that were created by the user”
To deploy add-ins / command extensions to the app catalog. You can’t deploy these in the modern view.
Managing permissions – Permissions for the list/library are much clearer. The Shared/Shared With is too awkward.
Manage files with no checked in version
Setting Column Default Values
View Settings
Display the Title in libraries
Create a “folderless” view
Add the list ID field to a view
When I need to “Display items in batches of the specified size” and defeat the endless, maddening modern scroll-delay-scroll-delay nonsense for large libraries.
Easier to sort column order, filtering, grouping and sorting on one single screen, as opposed to clicking around on modern. Usually for when you setup a list which is more than a few columns.
You import from Excel anything more than 5 columns you are going to want to sort out without horizontal scrolling and dragging and dropping, etc.
For grouping/filtering, I find the classic UX easier than JSON editing [Several people have said something similar]
Classic for lots of reasons (reindex, permissions, versioning, open behavior, etc.)
Fields that are not displayed but are used in formulas. I find it more reliable to select them in the classic view settings.
Display items in batches of the specified size
Displaying the version number in the view
Getting the URL of the view [This one bugs me, too. When you switch views, you get a URL like /AllItems.aspx?viewid=97fbd6f4-82a9-4916-9a1b-65ce28328173. You can’t know what the actual view page is without editing the view – i.e., going into the classic view settings.]
Navigating up the content type hierarchy
Group by collapsed
Multiple group by
I think the docicon field is only available in classic
Adding a site field (perhaps my knowledge is outdated here)
Sums (Totals)
Calendar stuff for me. If I remember correctly (because I’ve moved to other stuff) surveys and task views are also better in classic
I don’t think I see “display the version number of a file.” Drives me nuts that I can add Promoted State to a view in “modern,” but not Version. I like to see both for the Site Pages library.
Other
Creating new Document Sets doesn’t work in modern interface.
Mapping a geolocation field in a list. Cannot be done in modern SPO
To deploy add-ins / command extensions to the app catalog.
Editing columns in a content type. SharePoint knows there’s a gap because it gives me a link to switch.
Orchestry is a partner of ours at Sympraxis. (Ask us about them anytime!) We love their toolset and recommend them frequently to our clients to improve their governance and provisioning activities. The stuff they build is incredibly powerful – and reliable.
That’s why I was really surprised when I added the Orchestry People List Web Part to a site home page and I didn’t see the actual people, just what looked like placeholders for them. The counts were right, but no details. (This Web Part is better than the out of the box People Web Part because you can set it to show Site Owners and/or Site Members automagically.)
As I am wont to do, I asked my friends at Orchestry what the scoop was. Turns out, it wasn’t an Orchestry problem. The issue was rooted in a really bad user interface in the SharePoint Admin Center.
Here’s what happened. Way back in March, I installed the PnP Modern Search Web Parts. They are just about my favorite tools to use with SharePoint, and I install them as soon as I start working in a tenant. In many cases, I don’t bother the Global Admin to approve the Microsoft Graph permissions for PnP Modern Search, because we can accomplish what we need just using SharePoint Search.
In the screenshot below, you can see the pending request for PnP Modern Search (red box) as well as the Approved requests which Orchestry needed (green box).
The unfortunate part of his UI is because PnP Modern Search “asked” for User.Read.All first, it still “owns” that request.
What we should have seen in the UI was four requests for Orchestry, like so:
Because PnP Modern Search had already asked for User.Read.All, there were only three. The Global Admin approved the three and we called it a day.
Note what happens in that UI after the requests are approved. We lose all the info about which app needed the permissions and when they were granted. No bueno.
Leaving the security implications of all this aside (but keeping in mind this UI exists only for security purposes!), there’s no way for us to see what solution requested the API access, when it was requested, or who approved it after the fact.
Believe it or not, the solution to fix the Orchestry Web Part issues was to approve the User.Read.All permission for PnP Moden Search. That makes sense, right? (No, no it doesn’t at all.)
Another day, another opportunity to spackle the walls of SharePoint where there’s a hole. Wouldn’t it be great if you could go somewhere in the SharePoint Admin Center to see all the places you’ve used a particular Web Part in pages? Well, you can’t, so PowerShell. PnP.PowerShell, in fact.
You may be considering migrating your videos from Stream Classic to Stream in SharePoint. After all, it’s got to happen sooner or later, and the migration tool is now available for everyone. At Sympraxis, we’ve started to help our clients with these migrations, usually in the context of other work.
One thing you’re likely to want to know is where you have used the Stream (Classic) Web Part in your pages. Depending on how you do the migration, it is likely going to be a good idea to visit many of those pages to either switch to the Document Library Web Part or at least validate the Stream (Classic) Web Parts are working.
I took a bit of a different approach with this iteration, though. Rather than using search to find the pages with the Web Parts (I found it was missing some), I switched to using Get-PnPPageComponent | PnP PowerShell. This allows me to get the Web Parts (aka Page Components) in a page directly.
The script below is what I used. I’ve included some comments to indicate where you might choose to do things a bit differently, depending on your environment and your goals. I’m just outputting the info to the console, as the tenant where I’m working isn’t that dense. You may choose to output to a CSV file or something else.
# findStreamClassicWebParts.ps1 - Inventory Stream Classic Web Parts to ensure they still work after migration
# Connect to your tenant here. This should be the only change you need to make to use this script.
$tenant = "sympmarc"
$adminConnection = Connect-PnPOnline -Url "https://$($tenant)-admin.sharepoint.com" -Interactive -ReturnConnection
# Get all the sites to check
# Checking all the Communication Sites and Team Sites
# $sites = Get-PnPTenantSite | Where-Object { $_.Template -eq "SITEPAGEPUBLISHING#0" -or $_.Template -eq "GROUP#0" }
# Checking sites associated with the Intranet (Home Site)
$sites = Get-PnPHubSiteChild -Connection $adminConnection -Identity "https://$($tenant).sharepoint.com" | Sort-Object
# You may choose to exclude some subsets of sites
$filteredSites = $sites | Where-Object { $_ -eq "https://$($tenant).sharepoint.com/sites/Exec-BoardRelations" }
foreach ($site in $filteredSites) {
Write-Host -BackgroundColor White -ForegroundColor Black "Looking in $($site)"
# Get the pages
$siteConnection = Connect-PnPOnline -Url $site -Interactive -ReturnConnection
$pages = Get-PnPListItem -Connection $siteConnection -List "Site Pages" | Where-Object { $_.FieldValues.File_x0020_Type -eq "aspx" }
foreach($page in $pages) {
#Write-Host -BackgroundColor White -ForegroundColor Black "Checking $($page.FieldValues.FileLeafRef)"
$streamPage = Get-PnPPageComponent -Connection $siteConnection -Page $page.FieldValues.FileLeafRef | Where-Object { $_.Title -eq "Stream" } | Select-Object Title, WebPartId
if($streamPage) {
Write-Host -BackgroundColor Green -ForegroundColor Black ">>> Found Stream Classic Web Parts in this page: $($page.FieldValues.Title) - $($page.FieldValues.FileDirRef)"
}
}
}
When you set a User Profile property with PowerShell, and it has a format of Date no year, how should you pass in the values? In other words, do you pass in the year, and it just gets lopped off or do you just pass in the day, like “November 10”?
In many cases you won’t actually care that much. But you should want to know how the values are stored so it doesn’t bite you later.
Out of the box, SPS-Birthday is the only User Profile property which uses the Date no year format. But you may decide to add others.
When you run a line of PowerShell like this, the value stored in the SPS-Birthday field is “11/15/2001 12:00:00 AM”
In the user interface (UI), in both the cases above, the result is:
What this proves in the Date no year format stores the value as a full date/time, but it is displayed without the year.
The reason this mattered to me was I was trying to use the PnP Modern Search Web Parts to display people with birthdays today. To do that, I needed to filter on a Managed Property mapped to the People:SPS-Birthday Crawled Property.
Once the RefinableDate02 Managed Property was set up, I could add a query like this:
Note that the year needs to be a constant in order for the filter to work. We might have two people with birthdays on November 15, but in the years 1954 and 2001. If we store the actual year with in the SPS-Birthday property, then our filter can’t work: there’s no wildcard-type mechanism in KQL we can use to say “ignore the year”. In the old DOS days, we could do something like:????-{CurrentMonth}-{CurrentDate}T00:00:00Z to say “any four characters”, but KQL doesn’t support that.
So, the key here is to choose a year and set all the birthdays to have that constant year. In this case, we chose 2000. This also ensures data privacy, as someone with a little PowerShell knowledge could retrieve people’s “real” birthdays.
If we didn’t choose a constant year, but instead passed in “November 15” and expected that the year wasn’t stored, our filter would break when 2023 rolls around.
Taking advantage of the Content Type hierarchy is an important part of a powerful information architecture, regardless whether you’re working with documents, list items, pages, etc. I’ve talked about this in conference sessions for years, but it doesn’t seem like I have a blog post about it.
Let’s use this slide from one of those sessions to illustrate the points.
SharePoint gives us the Document Content Type “out of the box”. Every Document Library you create in SharePoint (assuming you don’t use some fancy template) has the Document Content Type enabled for it. So many people just start dumping their files into the Documents (aka Shared Documents) library with every file becoming a Document and then wonder why no magic is happening.
In the example above, I have two interstitial Content Types. (Interstitial – or interstices – are spaces between things.) I create these interstitial Content Types, but never enable them in a Document Library; they generally only exist to create a strong hierarchy.
Org Base Document – When I start setting up the information architecture in a tenant, I almost always create a Content Type like this, usually putting the name of the organization in place of “Org”. You may never touch this Content Type again after you create it, but I’ve had it save my bacon multiple times when someone says something like, “let’s add X to ALL our custom Content Types”.
Contract – This is also a Content Type which I may not ever enable in a Document Library, but it allows me to search for Content Types which inherit from it.
These days, I’m most likely to create Org Base Document and Contract at the tenant level (in the Content Type Hub, via the Content Type Gallery in the SharePoint Admin Center). We use that enterprise level capability for Content Types which *may* be used in one or more sites. It gives us a central place to manage our information architecture – where it makes sense to do so. Since I’m going to inherit from Org Base Document for all my custom Document-derived Content Types, I create it at the tenant level.
When we set up a custom Content Type and inherit from an existing Content Type, there’s a brilliant logic under the covers. The out of the box Document Content Type at the tenant level has its ContentTypeId =0x0101. 0x0101 represents a Document in every tenant. (See: Base Content Type Hierarchy | Microsoft Learn for the full list of base Content Types in SharePoint.)
When I create the Org Base Document Content Type in the Content Type Gallery, it gets a ContentTypeId which starts with 0x0101 and then has a unique GUID-like part. Here is the full hierarchy tree for the Content Types with their ContentTypeIds in my Sympmarc tenant:
Content Type
Inherits from
ContentTypeId
Item
[System]
0x01
Document
Item
0x0101
Sympmarc Base Document
Document
0x0101002FBDBE6A1A315F438E41F10681463A61
Contract
Sympmarc Base Document
0x0101002FBDBE6A1A315F438E41F10681463A6101
Employment Contract
Contract
0x0101002FBDBE6A1A315F438E41F10681463A610101
Real Estate Contract
Contract
0x0101002FBDBE6A1A315F438E41F10681463A610102
As you can see, the inheritance model makes a lot of sense. Each inheritance appends something unique to the ContentTypeId. Once I’ve enabled the appropriate Content Types in Document Libraries (in this case), I can take advantage of the hierarchy using queries like:
Intent
Query
Show me all my custom Content Type -based documents
Show me all the Contracts is the really powerful query here, IMO. By requesting all content with a ContentTypeId which starts with the Contract Content Type’s ContentTypeId (That’s what the asterisk does for us.), it doesn’t matter if I create a new Content Type inheriting from Contract. The query will automagically continue to do what I want because the next Content Type inheriting from Contract will have a ContentTypeId of 0x0101002FBDBE6A1A315F438E41F10681463A610103. In other words, the ContentTypeId:0x0101002FBDBE6A1A315F438E41F10681463A6101* query will just pick that new content up for me without any adjustment.
Pair this good information architecture with the PnP Modern Search Web Parts, and you can build search-driven experiences which are highly specific, easily maintained, and extremely reliable. This is NOT “just Google”. It’s you building solutions to match the user stories and content needs in YOUR organization.
If you extrapolate from these examples, you probably can imagine some potential hierarchies in your information architecture which may help you create more powerful solutions for your end users. I’m curious about your thoughts, so please comment if you have examples.
In a modern SharePoint site, we only get one Site Pages library. We can’t create additional libraries which contain aspx pages which act like that special Site Pages library. If we could, we could meet a whole lot of interesting use cases, but it’s not an option.
One thing we *can* do is add additional Content Types to the Site Pages library. In many cases, I’ve seen people add a new column or two to the Site Pages library, but I don’t often see them adding additional Content Types. By using Content Types, you can take advantage of a richer information architecture which can span multiple sites, if needed.
Content Types are one of the primary building blocks for SharePoint, and I can’t gush about them enough. I do entire conference sessions just about Content Types! If you’re not using Content Types, you’re not holding SharePoint right, if you ask me.
I won’t go into how to create Content Types in general, but in this case, you’ll want to follow steps similar to this:
If the Content Type will be used only in the specific site (maybe a Benefits Overview in the HR site), then create the Content Type in the site. If there’s even a remote chance you’ll use the Content Type across sites (maybe Team Member Intro), then build it in the Content Type Hub in the SharePoint Admin Center.
To add a Content Type to the Site Pages library, you’ll most likely want to inherit from the out of the box Content Type called Site Page. This is the Content Type which is available in the Site Pages library by default.
In the example below, we’ve got three custom Content Types enabled on the Site Pages library: Productivity Tip, How To Guide, and Troubleshooting Guide. We’ve defined those Content Types at the tenant level: in the Content Type Gallery in the SharePoint Admin Center. Each of these Content Types inherits from an interstitial Content Type called Base [ClientName] Site Page, which inherits from Site Page. The reason we have the interstitial Content Type is so that we can say “show me all of the content which inherits from this Content Type”. That way, we can add additional peers to Productivity Tip, etc. without reconfiguring our result locations. We also have several Site Columns added to each of these Content Types so we can categorize the pages to improve findability.
Here’s what the Site Pages library looks like in one of our sites:
As you can see, the three custom Content Types are available there, and we can declare any Site Page as one of these special Content Types.
From here, we can use the PnP Modern Search Web Parts (my favorites!) to build powerful and sophisticated “slicing and dicing” for the content here or across multiple Site Pages libraries. But that’s a post for another day…
Caveats
If you change the Content Type of a page or set a column value, you’ve tacitly edited the page. That means you need to republish the page in order to make the change(s) visible – and even more importantly, to get the search crawler to pick up the change.
Because you’re adding Content Types into the Site Pages library, you may need to get creative with views in the library. At the very least, I generally change the default view from By Author (which is rarely helpful, anyway) to By Content Type. But build views which represent the tasks you want to complete: Pages which haven’t been reviewed, By Hardware Type, etc.
Recently, I wanted to create some new Content Types for videos in a SharePoint site.
In the old days, we would inherit from the Video Content Type and create our own variations on that theme, maybe Company Meeting Recording, or Team Meeting, whatever “flavors” of Video we might have. I admit that thinking preceded the concept of Stream, nee Office 365 Videos.
The Video Content Type has a number of Site Columns included in it to capture details about the Video. Useful enough, but not stuff most people would bother tracking.
As I am wont to do, I went straight to the source and pestered Marc Mroz (@MarcMroz) on Twitter.
YES! Just inherit from the normal Document CT! We aren't adding a new CT for Stream on SharePoint, everything we are adding works just from the normal document CT. The old "Video set" stuff is NOT being used going forward.
As Marc pointed out, with Stream in SharePoint we can simply inherit from the Document Content Type and the magic happens with the Stream smarts right in the Document Library.
Content Type inheritance is an important thing to learn more about, but the key message here is we no longer need to treat videos as special snowflakes; SharePoint understands what a video is and how to handle it. Videos are now first class Document Library citizens.
I admit this post has been languishing in my drafts for a while now. What triggered me to finish it and get it posted is the announcement that the Stream Classic to Stream in SharePoint migration tool will soon by available to everyone as a Public Preview. Start planning for your migrations out of Stream Classic. Don’t be left behind!
The Microsoft Stream (Classic) to SharePoint migration tool will be enabled for everyone the beginning of October as a Public Preview. Start preparing to migrate. We’ll announce the 1 year notice for retirement when tool switches to GA. https://t.co/7ROHHJTaOY
It would seem like the simplest thing in the world: show results in the PnP Modern Search Results Web Part in alphabetical order. My wanting to do this led to multiple conversations with my search guru Mikael Svenson (@mikaelsvenson) and the uncovering of some really useful variants on RefinableString in the SharePoint Online Managed Properties.
The new(ish? – it’s not clear how long they have been there) pre-created Managed Properties which are variants of RefinableString are now documented in Manage the search schema in SharePoint – SharePoint in Microsoft 365 | Microsoft Learn. Until I offered some updates recently, these variants weren’t in the article. I’m not sure I’d ever found this article before, but it seems to be the canonical list of Refinable Managed Properties, along with a lot of useful information about the Search Schema.
The new (to me, anyway) ones are in the last four rows of the following table in that article:
Managed property type
Count
Multi
Query
Search
Retrieve
Refine
Sort
Managed property name range
Notes
Date
10
–
Query
–
–
–
–
Date00 to Date09
Date
20
Multi
Query
–
Retrieve
Refine
Sort
RefinableDate00 to RefinableDate19
Date
2
–
Query
–
Retrieve
Refine
Sort
RefinableDateInvariant00 to RefinableDateInvariant01
*
Date
5
–
Query
–
Retrieve
Refine
Sort
RefinableDateSingle00 to RefinableDateSingle04
Decimal
10
–
Query
–
–
–
–
Decimal00 to Decimal09
Decimal
10
Multi
Query
–
Retrieve
Refine
Sort
RefinableDecimal00 to RefinableDecimal09
Double
10
–
Query
–
–
–
–
Double00 to Double09
Double
10
Multi
Query
–
Retrieve
Refine
Sort
RefinableDouble00 to RefinableDouble09
Integer
50
–
Query
–
–
–
–
Int00 to Int49
Integer
50
Multi
Query
–
Retrieve
Refine
Sort
RefinableInt00 to RefinableInt49
String
200
Multi
Query
–
Retrieve
Refine
Sort
RefinableString00 to RefinableString199
String
40
Multi
Query
–
Retrieve
Refine
Sort
RefinableStringFirst00 to RefinableStringFirst39
*
String
10
Multi
Query
–
Retrieve
Refine
Sort
RefinableStringLn00 to RefinableStringLn09
**
String
50
–
Query
–
Retrieve
Refine
Sort
RefinableStringWbOff00 to RefinableStringWbOff49
***
String
50
Multi
Query
–
Retrieve
Refine
Sort
RefinableStringWbOffFirst00 to RefinableStringWbOffFirst49
*, ***
* Mappings to crawled properties – Include content from the first crawled property that is not empty, based on the specified order. ** Language neutral word breaker *** Complete Matching
As you can see, each of the additional RefinableString* Managed Properties has something a little different about it, as indicated in the Notes.
Need to know more? Feel free to ask your questions in the comments.
Modern SharePoint gives us few options from a design perspective. That sounds horrible, right? Actually, I see it as a very positive thing. By taking the pixel-pushing out of the mix, we can get people to focus on CONTENT, which is the do or die part of any Intranet.
Over the years, I was able to do a LOT of business (as Sympraxis) making “SharePoint not look like SharePoint”. But that came at a cost on several levels:
My clients had to pay me to implement a design, which often wasn’t well-suited to SharePoint in the first place. Anyone remember the agony of getting rounded corners on Web Parts in the old days with just CSS?
We focused a lot on that design, and far less on the content which we’d pour into it. Oftentimes, after we finally got the pixels in the right place, the content wouldn’t even fit into the pages very well!
Modern SharePoint gives us a clean, responsive, open user experience (UI) which few people quibble with. Years ago, many people driving Web site development had come from the print world, so there was a lot of that paper-perfect thinking in the mix. These days – a statistical generation later – most people driving the projects have always worked in the Web. The scales have tipped.
If you look at the SharePoint look book, virtually every one of the designs you see can be accomplished with out of the box settings. That’s a lot of power.
In modern SharePoint, there are several “design surfaces” we can work with:
Imagery – We can create one or more Organizational Assets libraries to contain organizational imagery. These libraries might contain logos, product images, paid stock images, etc., and are available to all Site Owners when they build their sites. See: Create an organization assets library – SharePoint in Microsoft 365 | Microsoft Docs
Iconography – SharePoint gives us a set of icons we can use for things like Quick Links, but we can also use custom images. For consistency, and simplicity, the out of the box set usually suffices. See: Fluent UI – Styles – React – Fluent UI Icons (microsoft.com) (Scroll down to see the icon set.) The Flicon – Fluent UI Icon Search site makes it easier to search that icon set (maintained by fellow MVP Chris Kent [@theChrisKent]).
Site Icons – If your department or working group has a logo, the site icon can be that logo. The Intranet itself can have a logo, too. These logos are best created as square images which look good reduced to a fairly small size. In other words, not a lot of fancy details. See: Change the look of your SharePoint site (microsoft.com)
Page Layouts – This starts to get more into content, but page layouts can be preconfigured for specific types of content, to ensure consistency and familiarity. See: Page templates in SharePoint (microsoft.com)
Anything beyond these design options usually would require custom coding and frankly often isn’t worth the effort. Having a designer available to make suggestions doesn’t hurt, but sometimes they get frustrated by the lack of options! These aren’t old-fashioned Web sites which required us to create an HTML template and CSS in order to get rolling. We can create a site and start working on the content right away, shortening the path to success immensely.
SharePoint Location columns have some issues which are pervasive and repeatable. One of the purportedly biggest benefits of using one of these columns is we can type in an address, and it’s looked up in Bing Maps. What’s supposed to happen is the details of that location are then parsed out and made available for use as separate “pseudo-columns”. For example, we might type in ‘111 Coleman Blvd, Savan’ and then select the corresponding location.
Then in the list where we have the column, we can choose to display the City, State, Postal Code, etc. without ever entering them.
All well and good. Many addresses, though, come back with missing parts of their data. In this case, there is no latitude/longitude, which we’d like to have for mapping. This is one if the big benefits of using a Location column! There’s no way to discern in the UI whether the variants one might see for an address is “better” than another. In this case, choosing ‘111 Coleman Blvd, Pooler, GA 31408’, which is the exact same building, yields the latitude/longitude.
But there’s no way in the UI to discern this as we are making the selection. I can only figure it out if I go to the Bing Maps UI and look for the latitude/longitude at the bottom of the left panel.
Alas, my excitement at realizing this variant on the address has a lat/long was short-lived, as the values are not pulled into the list on selection.
As you can see below, even though I have selected an address which has all the values in Bing Maps, only a few of the pseudo-columns are populated. The lat/long are missing as well as the Postal Code.
It’s also very easy to “select” a value which isn’t even coming from Bing Maps. In other words, I can type ‘111 Coleman Blvd’ and hit enter, and that value is stored in the column with no warning that it isn’t “connected” to Bing Maps. Or, even worse, one of the values from the dropdown is selected, and may well be wrong. Here, I’ve mistakenly typed ‘1111 Coleman Blvd’ and I just get that untethered value and there’s nothing in the UI to make that clear. (Yes, the second line is blank, but you only see that if you edit the column.)
Another small annoyance is once an address is selected, we can’t copy it from the field in edit mode. We can’t highlight the address anywhere in the UI I can find. So, if we want to grab that address, there’s no way to do so.
And so forth…
What’s your luck been with Location columns? Do you find you consistently get valid – and useful – data back from Bing Maps?
PnP.PowerShell is one of my favorite tools of the trade. I’ve had to set up multiple machines for myself or others for this lately, and I always find myself looking for the fastest path to glory. Usually, it takes about 9 articles and 15 blind alleys, so I figured I’d capture what seems to work for me. Hopefully I can keep this up to date if things change.
Install Visual Studio Code
Visual Studio Code aka VS Code aka VSCode aka Code (which I’ll use in the rest of this post) is the “modern”, free code editor from Microsoft. I’ve used dozens of code editors over the years and Code is one of the best. Plus, everyone else is using it!
This one gets me every time. You’ll want your Execution Policies set like this:
This allows you to install PowerShell modules with less friction. It’s possible your organization won’t let you make this change. You can see your current settings by typing
Get-ExecutionPolicy -List
in a terminal window. To open things up, run this cmdlet:
I’m sure there are reasons to set this in different ways based on your organization’s view of security. I’m not going to get into that here: heed your governance rules.
Install PowerShell 7
If you’re running a Windows machine, you’ve most likely got PowerShell 5 (PS5) installed by default. PowerShell 7 (PS7) has more capabilities and is required for PnP.PowerShell to run successfully. Some cmdlets may run just fine with PS5, but don’t be fooled: you want PS7.)
One of the great things about Code is the rich ecosystem of extensions. The PowerShell extension from Microsoft makes Code smart about PowerShell. You want it.
Open the Command Palette on Windows or Linux with Ctrl+Shift+P. On macOS, use Cmd+Shift+P.
Search for Session.
Click on PowerShell: Show Session Menu.
Choose the version of PowerShell you want to use from the list.
You’ll want to choose PowerShell (x64), if it isn’t already selected.
Pro tip: When you’ve got a PowerShell file (.ps1, .psm1, etc.) open, you can also get to the PowerShell Session Menu by clicking on the squiggly brackets next to PowerShell in the bottom toolbar. Plus, the version is there!
Install PnP.PowerShell
Finally, the piece de resistance: PnP.PowerShell. This is the module that lets us do so much with Microsoft 365. If you’re using the SPO module instead, I say switch.
You need to run Code as an administrator if you want to install modules. To do this, I usually just type Code in the search box in Windows 11, right click the result, and choose Run as administrator.
This article is for those of you on a Windows machine. I don’t have a Mac, nor do I want a Mac. I also don’t run Linux. Or a Sinclair Z-80 (though I loved the one I had way back when, it wouldn’t run PowerShell).
I expect I’ve missed a few little bits here. Feel free to tell me so in the comments, and I’ll make updates. Also, let me know if this is helpful!
Lately, every time I have shared my screen on a Teams Meeting with other Sympraxians, they have told me my screen looks awful and I should stop sharing. It sounded unpleasant, but I couldn’t see what they were seeing.
Today, with help from the awesome Emily Mancini (@eemancini), I figured out the issue. From a few searches, we found there are several suggested fixes out there.
Disable GPU hardware acceleration – This tells Teams not to use the Graphics Processing Unit (GPU) to speed up stuff on the screen. To do this, you click on the ellipses in the upper right of Teams / Settings.
Note that you must restart Teams for this to take effect. This means right clicking on the Teams icon in the system tray and then Quit. This seems to fix a similar issue for many people out there.
Delete the Teams cache – This takes a little more fortitude, and I’m not positive it would help, but the instructions are in the article below.
But neither of these approaches solved the problem for me. Each time I would do a test call with Emily, I would get the equivalent of the vomit emoji.
I thought back over the timeframe when they had been telling me things were ugly. I recently reconfigured my screens, so I had been fiddling with the settings for each of them: my laptop screen and two external monitors.
As I poked around in those settings, I remembered I’d enabled an appealing-sounding setting called HDR for my laptop screen. This is a setting in Windows 11 that purports the following:
HDR content on Windows offers better brightness and color capabilities compared to traditional content (sometimes called standard dynamic range [SDR] content). Traditional content typically shows details in a bright part of a scene or a darker part of a scene, but not in both parts at the same time. For example, if the shot focuses on a bright window in the scene, details in the shadow are lost.
Sounds nice, right? Well, that was a change, and it had to do with my screen. I disabled HDR and pinged Emily – one last time as it turned out.
Moral of the story: Teams doesn’t seem to play well with this HDR setting. Don’t turn it on for a screen you’d like to share in Teams (my other two screens had been fine for sharing). Thanks, Emily!
Working in a client tenant today, I noticed a new suggestion in the SharePoint Admin Center I hadn’t seen before. It’s certainly possible it’s been around for a while, but it was new to me.
The tenant where I’m working has been around since at least 2015. That means it was created with a classic SharePoint site as the root site. Back then there was no such thing as a modern SharePoint site, and Microsoft hasn’t forced us to replace what we got when we created the tenant. In the message above, they are suggesting site modernization. Interesting, I thought.
We do this modernization all the time as we help people build new Intranets or try to get more from their investment in the Microsoft 365 platform. It’s not at all unusual for an organization to have been using Microsoft 365 for years but maybe only using it for Exchange. Or maybe using one Document Library in the root site to store ALL the organization’s documents. Believe me, we’ve seen all sorts of things which would surprise you. It’s one of the reasons we’re so passionate about the Microsoft 365 Maturity Model.
The great thing about this recommendation is it actually steps you through the process. I figured it might be useful to know how it works for those of you who aren’t into the platform up to your elbows like we are.
When you click the View recommendation button, you get a nice set of suggested steps to follow to set up and configure a new modern site for the root.
Here are those steps and links. Generally, organizations will want a modern Communication Site in the root of their tenant. This site tends to be the launchpad for the organization’s Intranet. At the very least, it can be a launchpad into Team Sites where people do their work.
Plan the site content. This step includes understanding the goals of stakeholders and the needs of users. Learn more
Build the site. Create a communication site, customize the design, and add your content. Learn more
Prepare to launch. Set site permissions and test the site. Learn more
The process of building out the new Communication Site need not be complex, but usually you’re planning an entirely new Intranet or set of access paths for folks to get their work done. Thus, while building the new site isn’t complex (you can create a new Communication Site in about 7 seconds!), deciding how it should look, what navigation it should have, what content it should house, etc. can take some real planning. Let’s gloss over all that, shall we?
Once you have the new Communication Site – we tend to build it in a location like /sites/NEWIntranet – you’ll come back and click the Replace root site button. On the following screen, you simply provide the URL to the Communication Site you’ve just built. Note the caveats: the new site can be a Communication Site or a Team Site, but it can’t be a Hub Site or connected to a Microsoft 365 Group. This is because swapping a site into the root means fixing up a lot of links and other stuff under the covers.
Also note the existing root site will be moved to an archive location. You won’t lose the site or its contents – but it won’t be in the same spot anymore.
At this point, I’ve stopped my exploration because clicking that Save button is going to initiate what we call a “site swap”. Once it’s done, your new site will be in the root location, the old site will be in the archive location, and everything should work just great. You’ve put a whole communication plan about this is place before you click the button, right?
If you want more control over this process, you can do what some of us have been doing for several years now: you can use the PnP.PowerShellInvoke-PnPSiteSwap cmdlet. You still build the new site in a new location, but when it comes time to do the site swap, you simply call the cmdlet, something like this:
This may appeal to you because you can specify the archive destination, but the net effect is exactly the same. Note that you can only use this cmdlet to swap a site into the root; you can’t arbitrarily swap two other sites. (You can however, rename them.)
It’s great to see suggestions like this coming into the admin UIs. It’ll help a lot of organizations with a support staff of one – and there are more of those than most people think!
Another day, another weird SharePoint problem. We’re working on a client Intranet, and just launched it yesterday. Everything works great and the client is happy. But, as is often the case, we needed to make a few tweaks to the Site Nav based on user feedback. (An Intranet is never done, folks.)
If you look at the screenshot of the site banner below, you can see the issue. There are extraneous icons on the far right, and the Edit link is missing for the Site Nav. That means we can’t make any changes to the Site Nav. There’s no settings page for it and there’s no workaround that I could find.
That is, until I posted the quandary to my MVP channels and Cathy Dew (@catpaint1) gave me an obscure fix. (I shouldn’t need to know exactly who on the Product Team to contact when something like this happens. It’s a bug and it should be fixed. I feel for all of you out there who must go into support ticket hell to resolve these things.)
If you ever get yourself into this state – and if you use Edge, you may well get here – this will fix things for you. (Another option is to switch to Chrome, but that feels wrong.)
This may happen to you if the following things are true:
You use Edge as your browser
You’ve enabled Viva Connections
You’ve opened the Intranet in Microsoft Teams in the browser
You’re in any SharePoint site and want to edit the Site Nav
The people who manage the Intranet are the most likely to do all of these things, and in the sequence that causes the problem.
The fix is simple if you’re used to the tooling but may not be familiar to you if you haven’t used the Developer Tools. Trust me, it’s not as bad as it looks. Here are the steps.
Open the Developer tools in your browser by hitting the F12 key
If you haven’t ever done this before, you’ll get this dialog:
Click the Open DevTools button. If you’d like to avoid this dialog in the future, check the Remember my decision box first.
The Developer Tools will open on the right side of the screen (by default – if you’ve used them before, they will be wherever you last docked them).
Click the Console tab. If your screen isn’t very big, you may need to click on the >> to see the tab.
At the bottom of the panel, you’ll have a command prompt. (You may well see a whole bunch of errors and other junk in that panel. To be honest, Microsoft does a horrible job cleaning up their debugging messages and spurious errors. But that’s a diatribe for another day.)
Type localStorage.hostedApp and hit Enter. If you see the 'viva1p' value like I do below, that’s the culprit.
Type localStorage.hostedApp = null and hit Enter. You’ll see the value is now null.
Refresh the browser and all shall be good with the world – until the next time you open Microsoft Teams in the browser and navigate to the Intranet. (I’ve been sitting here toggling this back and forth to test it.)
What’s happening here is Microsoft Teams – the Viva Connections app in Teams, actually – is setting a value in the localStorage of your browser. That value for hostedApp tells SharePoint to render the page in a Teams-friendly way, with those extra icons on the right. When we go to the site in SharePoint instead, that value is still set, and the page renders with those icons. It also happens to break the Edit link for the Site Nav.
This fix ONLY sets the localStorage.hostedApp value in your browser to null. It doesn’t do anything dangerous or scary.
Note that this is a BUG. Microsoft knows about it, and one might hope they will fix it – soon. Until then, we have this fix.
This is something which has come up in several contexts in the last few months, so I figured I’d put virtual pen to virtual paper and record it for future me and all of you.
If you’ve ever tried to embed content from an external source in a SharePoint page using the Embed Web Part, you may have gotten an error similar to this:
Embedding content from this website isn’t allowed, but your admin can change this setting. They will need to add ‘<specific URL>’ to the list of sites that are allowed.
It looks something like the screenshot below. It doesn’t matter if it’s a “bare” URL or you’ve encased the URL in an iframe explicitly, like I have below.
When you use the Embed Web Part, SharePoint takes the URL you provide and wraps it in an iframe. An iframe is a way for the browser to display the content inline but protect the page from any malicious actions the embedded site might try to take when it loads. Think of it like displaying a scorpion in a glass box. The scorpion may not have any venom, but since you don’t really know, you leave it in the box. You can see it just fine, but it can’t hurt you.
It turns out the links below the error explain the solution, but I had never clicked those links and read the details! In fact, unless it was years ago, I’ve just ignored the setting we need to solve this.
If you’d like to embed content from a URL, you’ll need to make sure you’ve added the domain name in the site settings. To do this, click on the cog / Site information / View all site settings / HTML Field Security. Here, you can add the domains you’d like to allow to be embedded.
Microsoft provides a default set of common domains, which as of this writing and in my tenant is the following. It’s a bit of an archeology lesson to read through them all!
youtube.com
youtube-nocookie.com
player.vimeo.com
bing.com
office.microsoft.com
officeclient.microsoft.com
store.office.com
skydrive.live.com
powerbi.com
powerbigov.us
sway.com
docs.com
microsoftstream.com
powerapps.com
flow.microsoft.com
powerapps.us
flow.microsoft.us
app.smartsheet.com
publish.smartsheet.com
www.slideshare.net
youtu.be
read.amazon.com
onedrive.live.com
www.microsoft.com
forms.office365.us
support.office.com
embed.ted.com
channel9.msdn.com
forms.office.com
videoplayercdn.osi.office.net
sway.office.com
linkedin.com
web.yammer.com
customervoice.microsoft.com
You can add the domain you’d like to use in the settings. Once you’d added it to the site, you can embed content from that domain – including its subdomains – in the site with the Embed Web Part.
Note that this is a per site setting. If you want to embed content from the same domain in multiple sites, you’ll need to add it to each site. As far as I know, there’s no programmatic way to add a domain across sites, but I could be mistaken about this.
If you’re feeling loosey-goosey, you could change the setting to allow embeds from any domain, but you may not want to do that for security reasons.
Finally, you must be a Site Owner to change these settings. If you don’t have access to this setting, you’ll need to get help from someone who does.