Tag: Gist

  • Gist extension for VS Code

    Gist is a big part of my workflow. I’ve always been on the lookout for a desktop tool. Well, it turns out I was looking in the wrong place. A few days I discovered two VS Code extensions Gist and GitHub Gist Explorer. They both really great extensions and so far I’m really enjoying having Gist at my fingertips in VS Code.

    vscode-gist-profiles
    Gist
    Screenshot
    GitHub Gist Explorer

    Once you have installed the extension you will need to grab yourself an access token for GitHub – you can get a personal access token by going to settings and then accessing Developer Settings. Make sure you just give the token access to Gists.

    GitHub Gist Explorer seems to be my favourite so far. Having the explorer available in the side pain really helps and the command bar paste and save from clipboard features are really great!

    How do I use Gist

    The majority of my use if private where I have created secret Gists. I have a number of markdown Gists which I have named Gistsmarks which I use as bookmarks to jump to a collection of Gists like boilerplates for my blog articles.

    Any code I share on my blog is shared as a Gist. This allows me to embed the code in the article with a nice editor and provides me with version control too. When I started using Gists I painstakingly went through all my posts and converted any previous code blocks into Gists. Oh, and when I reference code I use the highlighted line(s) URL which is a cool trick too.

    Highlighting code in a Gist with the URL

    I also use Gist to share code and snippets with friends and customers (when it is appropriate of course). I also have runbook like Gists to quickly reference which provide basic lines of PowerShell to perform tasks and run common commands against Microsoft Teams in Office 365 for example. I also have a markdown file for each of my demo environments where I hold information about different personas I have created, areas where I have built out certain functionality and a basic changelog.

    While I’m on the topic of VS Code extensions I think I will write another post and share my favourites VS Code extensions.

    Hopefully, you got the Gist of things! See what I did there….I’ll get my coat.

  • Replicate SharePoint Hub site navigation to other Hub sites

    Replicate SharePoint Hub site navigation to other Hub sites

    I have been working with a large government department where Microsoft Services helped them transform their intranet to SharePoint Online leveraging all the modern capabilities available as well as rethinking what an intranet was. This work included envisioning to fully exploit all of Office 365 and a crucial Information Architecture (IA) design which also mapped their existing, complex and poorly performing hierarchy of subsites to several new hub sites and a completely flattened site structure. I hope to write more about this work soon.

    Primary intranet site navigation provider

    To create a consistent user experience, the look and feel for several hub sites was replicated, creating the feel of a single intranet. The hub site navigation also needed to be replicated, and that is wherein the challenge lay. The navigation cannot be easily copied from one site to another, let alone between hub sites, and thus this solution was born.

    We decided to create a dedicated hub site that only the intranet team can access. Within this hub site, we were then able to create the intended intranet navigation and get the headings, order and links just right without troubling anyone. Once we were happy with the navigation and testing successfully passed, the idea was that we would be able to replicate the hub site navigation to all our other hub sites. More importantly, the process could be easily repeated, keeping the intranet navigation updated across many hub sites. The task could also be automated but we opted to leave it as a manually initiated task.

    Script to replication hub site navigation to other sites

    The solution to the problem is a PowerShell script made up of two functions. One function that gets the navigation from the hub site acting as the master or primary navigation provider (supplied from a parameter). The other function replicates the navigation to other hub sites. The Get-SPOHubSiteNavigation function exports the hub site navigation from the site provided to a CSV file. The CSV file is then used by the Copy-SPOHubSiteNavigation function to replicate the navigation links to other sites. The process could be hijacked somewhat to add hub site navigation from a CSV file rather than reading it from an existing hub site. We found it was easier to get a feel for the navigation in a real SharePoint site rather than work with the information in a spreadsheet.

    Screen recording of the Copy-SPOHubSiteNavigation replicating navigation between two hub sites.
    Screen recording of the Copy-SPOHubSiteNavigation replicating navigation between two hub sites.

    You can also disable the export process by setting the -Export parameter to -Export:$false. This switch makes the Get-SPOHubSiteNavigation quite useful for displaying the hub site navigation in an accessible collection. This collection can be used with pipe functions, to filter or sort the collection, for example. This collection is also much easier to work with than using the Get-PnpNavigationNode -Location TopNavigatioBar -Tree cmdlet and parameters to show the full navigation structure.

    Hub site navigation collection displayed in a table.
    Hub site navigation collection displayed in a table.

    Observations

    As with many of my posts, I like to share any observations I have made during the work.

    • The cached Hub navigation appears to refresh every 30 minutes if left unchanged. You can force the cache to refresh by editing the links and then clicking cancel.
    • A JSON file gets saved to the /_catalogs/hubsite library which could be related to the cached navigation. It has a GUID based name e.g. 20d31c78-8a8c-499a-b953-ecc673344cef.json and appears to get modified by the System Account when changes to the hub site navigation are saved.
    • The cached JSON file looks like this:
    • This JSON file, in theory, could be read from a source site and the navigation element copied and injected into the JSON on a target site through manipulation of the JSON payload and the /_API/navigation/SaveMenuState API.
    • When trying to add a new navigation node using the Add-PnpNavigationNode cmdlet. If the parent node was invalid I got the following error. This wasn’t because the URL linked to a file or folder that didn’t exist as the error below suggests. Dave Garrad kindly calls out the fix for thisThe fix is to add -External to the Add-PnPNavigationNode cmdlet to tell PowerShell that the link is not in the same site collection.
    Add-PnPNavigationNode error
    Add-PnPNavigationNode : error when no resource or file is available from the Url supplied.
    • The script could benefit additional error handling, particularly to ensure the target site(s) provided are registered as hub sites.
    • Bonus tip! If you find you have lost changes to the navigation, this same JSON file is part of a library where version history is enabled. This file appears in hub sites and sites associated with a hub site. The version history might help you recover from any loss, but you won’t be able to restore it as the library appears to be read-only.
    Version history available for JSON file which I suspect ins the cached hub site navigation
    Version history available for JSON file which I suspect ins the cached hub site navigation

    I hope you find this post useful. As always #SharingIsCaring

  • PowerShell to manage the modern SharePoint Footer

    PowerShell to manage the modern SharePoint Footer

    So I’ll start by saying how bowled over I am by the engagement I’ve had after posting a tweet sharing that I had programmatically updated the new footer. For this, thank you, this kind of engagement and praise is why I love the community and contributing to it.

    My tweet sharing my excitement after creating the PowerShell to manage the modern footer in SharePoint Online.

    I want to share some numbers with you, which was one of the main reasons for pursuing this task.

    1. My customer had 180 sites where the footer needed configuring.
    2. Settings, change the look, select footer (three clicks)
    3. Enable footer, browser and select a logo and then upload it, enable displaying the footer name and then add the footer name text, apply settings and close settings (eight clicks)
    4. Edit links (one click), create a new link, add text, add a URL, save the link, then (four clicks) and then repeat for three other links (twelve further clicks). Then save the navigation (one clicks). Eighteen clicks for footer links.
    5. That is a total of 29 clicks per site — a total of 4,860 clicks for all these sites.

    Almost five thousand clicks is a lot of mouse work and this number doesn’t even factor in browsing to each site nor typing each of the values and URLs, which leaves a lot of room for errors too!

    Screen recording showing a click counter of the work involved in configuring the footer at scale.
    Screen recording showing a click counter of the work involved in configuring the footer at scale.

    #ThinkLean #WorkSmarterNotHarder

    To emphasise just why I wanted to achieve this programmatically. Colleagues started to perform this manually while I did some R&D. During this time a collection of sites were updated. However, even with signed-off designs, change request and final confirmation, the customer still changed their mind hence why I like to code and work lean. This kind of change is not uncommon. However, for any organisation that centrally manages sites, has a flattened modern information architecture (IA) or a Hubified intranet with many sites where a standard and consistent footer might be wanted, not being able to manage the footer through PowerShell or other means is a problem. Even managing variations of a standard footer is likely to be an issue for if there is no way to handle it programmatically. 

    The intranet team within a large government department I have been working closely with over the last two years are responsible for close to 200 sites. Performing tasks against these sites and making changes to them efficiently using PowerShell or other means is essential. Finally, we should not forget that trends, standards and regulations change over time. One such example is that regulations for internet and intranet sites within the government might require that certain information must be readily available from the footer, which would lead to a need for wide-spread changes. Even with their carefully designed flat site structure as part of their information architecture (IA) which leverages Hub sites this change is still site-by-site as the footer configuration is not inherited from a Hub site and therefore set independently.

    Exploring how the Footer works

    So with all this in mind, I started to explore how this could be achieved using PowerShell. The options and tools are limited and restricted given the environment. I felt that if this could be through the UI, then there must have been an API I could play with to achieve the same result. Off I went in my lab using Fiddler, SharePoint Online Client Browser, Edge Insider developer tools, Postman and VS Code. Troubleshooting and reverse engineering things is a childhood pastime. I found many other exciting easter eggs during this exercise that I hope to share too, but it was like there was a bounty on resolving the footer configuration.

    Colleagues in my team were also looking for a solution to similar problems for their customers. A colleague quickly followed up about some undocumented verbs he found in the site design schema (see Site design JSON schema on Microsoft Docs). This news also confirms what I learned from some PG colleagues. Sadly, the footer only has site design support right now.

    What I discovered through Edge tools that each change to the footer through the editing footer links or from the change the look pane invoked an API post to “/_API/SaveMenuState”. This post had a JSON payload with the configuration of the footer and also included any footer links. Win!

    Developer Tools in Edge where I discovered the call to /_api/navigation/SaveMenuState
    Developer Tools in Edge where I discovered the call to /_api/navigation/SaveMenuState
    Full JSON payload sent to the SaveMenuState API in Edge Developer Tools.
    Full JSON payload sent to the SaveMenuState API in Edge Developer Tools.

    Initial approach and exploration

    I took off with examples of this JSON payload that I obtained from Developer Tools after applying several different footer configurations. I started exploring how the API worked using Postman (this is an excellent tool by the way). To use Postman, I had to create an SP app to get an auth token from AAD so I could use Postman with SPO APIs. I gave the app full tenant access so I could explore things better. Getting started with Postman was a great lesson and one I want to share in a separate post, but Postman is now a vital tool for me when working with SharePoint Online. Once I had played with the API, I set out to achieve the same behaviour in PowerShell. 

    Screenshot showing a API test in Postman
    Testing different submissions to the API using Postman.

    PowerShell Script

    I set out with the intent to share my work as I know this was going to be of interest to lots of others. I decided to create multiple functions, so each part of the footer can be configured separately. I also built this with a view of contributing and sharing it as part of the SharePoint PnP module. The code is functional and not perfect. Let me walk you through it and share the entire thing at the end too.

    Note: the script uses the debug log the output information ($DebugPreference = "Continue").

    Enable/Disable SPO Footer – These two functions were created to quickly enabled and disable the footer with a simple function call.

    Get/Set SPO Footer (Logo and Text) – It is useful to be able to get the configuration of both the footer text and the footer logo. These functions can be helpful to validate the configuration, for example. These both exist in the JSON as nodes with consistent GUID for their “title” and similar they both have a “key” that identifies them. To simplify the functions I created an overarching function to run them all.

    Get/Add/Remove Navigation Links – This needs further work but it could leverage the existing Get/Add/Remove-PnpNavigationNode cmdlets. For my customer, I hardcoded the URLs in the JSON payload on line 417 but I provided a sample of how this looks on lines 408-415.

    Sample Usage – Examples of how this can be used has been provided lines 435-471 but here is an example of the functions.

    • Enable/Disable-Footer – Enables and disables the footer
    • Get/Set-SPOFooter – Get the configuration of the footer
    • Get/Set-SPOFooterText – Set the footer text
    • Get/Set-SPOFooterLogo – Set the footer logo
    • Set-SPOFooterLinks – Create footer links

    Full script

    Here is the script with all the functions and some examples. I’m going to reach out to Vesa and the PnP team to see about getting this included somehow. But, for now, enjoy. If you have any issues or comments, please let me know!

    Quirks to be aware of

    During my testing, I noticed a number of quirks I had to work around or that I want to share and make you aware of.

    1. When using Postman between sessions, you must refresh your auth token to continue accessing SPO.
    2. No value is available when reviewing $site.FooterEnabled using the Get-PnPSite cmdlet so you cannot check to see if the footer is enabled or not.
    3. When getting the footer, I noticed there seem to be several different states for how the footer configuration which might impact the GET functions however I have tried to mitigate this risk.
      1. Disabled
      2. Enabled with no configuration
      3. Enabled, but the JSON appears similar to when it is it disabled
      4. Enabled with configuration and or links.
    4. I noticed some sites had the footer enabled after the footer feature was released, while others it was disabled, this was why I built the function to check how the configuration of the footer.

    I hope you find this post helpful. Your engagement and feedback are what drive me to write posts like this, so please keep it up!

    #SharingIsCaring

  • Switch between modern SharePoint homepages using PnP PowerShell

    Switch between modern SharePoint homepages using PnP PowerShell

    This featured in Episode 44 of the SharePoint Dev Weekly podcast.

    I’ve been working on a modern intranet project amongst over projects for the last 12-months. This has been more about the transformation of content and business processes, rethinking information architecture and reimagining a modern intranet than it has been about custom development.

    I’ve been working on a modern intranet project amongst over projects for the last 12-months. This has been more about the transformation of content and business processes, rethinking information architecture and reimagining a modern intranet than it has been about custom development.

    We’ve recently been testing variations in the design of a homepage with different audiences. This side-by-side comparison has allowed end-user feedback, performance and accessibility testing. The same approach has also been useful for previewing and testing the capabilities of new features (like the new Yammer web part). The challenge comes when you need to promote or switch over one of these variations as the new site homepage. The homepage is the page users are directed to when first navigating to a site or clicking on the site logo. It is like as important as the index.html or default.aspx page existence to a website. Note that these variations of the homepage permit testing of content and not site configuration. To test navigation, theme or similar we have separate sites and environments for this purpose.

    To solve the problem switching the homepage from an existing page whilst preserving the home.aspx page name I’ve leveraged the SharePoint Pnp cmdlets to create a script that will rename or remove the current homepage (and can remove it through a toggle) and then rename an existing page to make it the new homepage.

    Set-SPNewHomePage script demonstration
    Demonstration of the Set-SPNewHomePage script in action.

    Use my PnP PowerShell script to replace the home.aspx page

    Alternative methods

    Change the default homepage through SharePoint

    Site Owners can use the out-of-the-box make homepage action to make any page the default homepage or welcome page. This is available from the toolbar in the site pages library. But this keeps the page name and means the default page is /sitepages/randompagename.aspx instead of the standard /sitepages/home.aspx that all sites have. From my perspective this is not great. Certainly, intranet-like sites should follow some basic content management principles. Call in a touch on the OCD side but consistently having a standard homepage is one of these for me.

    Screenshot of setting a new homepage in the site pages library.
    Screenshot of setting a new homepage in the site pages library.

    Change the welcome page site property through Site Settings or PowerShell

    Previously you could use the classic settings page (typically exposed by the publishing feature) or by browsing to /_layouts/15/AreaWelcomePage.aspx to make changes to the welcome page. This method no longer works and throws an error.

    As with the make homepage action describe earlier this changes the default homepage to the use the page name you have provided and means the site won’t be available if users have bookmarked the site with the page name (/sitepages/home.aspx) in the URL.

    Screenshot of the welcome page site settings page.
    Screenshot of the welcome page site settings page.

    What is my point? To this day can I still memory recall core settings pages. With these, you can quickly review or makes changes to settings pages rather than using the UI to navigate to them. This includes those that may no longer be exposed in the UI. Whilst my memory serves me well I don’t recommend this approach as these pages and settings are gradually being replaced with alternatives or removed by the SharePoint and Office 365 engineering team for a reason.

    Instead, you can also use Pnp PowerShell to change the site welcome page property. I’ve provided an example script below.

    As simple as my script is, it is the approach worth learning the most. I hope you find this article useful and as with all Pnp development effort. Sharing is caring!

  • Working with SharePoint’s Second Stage Recycle Bin in PowerShell

    Working with SharePoint’s Second Stage Recycle Bin in PowerShell

    I thought I’d share a PowerShell script that I’ve created to perform a few tasks against a Site Collection Second Stage Recycle Bin (SSRB) in SharePoint.

    Remove-SecondStageRecycleBinItems.ps1
    Remove-SecondStageRecycleBinItems.ps1

    The requirement was to delete items that were older than a set number of days from the Second Stage Recycle Bin (SSRB). A record of each item deleted also needed to be added to a report.  But SharePoint can do this already I hear you say…well yes if a Site Collection quotas and the auditing features are used. In this scenario neither could be.

    To display items in the Second State Recycle Bin in a table I used this command.

    $site.Recyclebin | where { $_.ItemState -eq "SecondStageRecycleBin" -and $_.deleteddate -le $dateDiff} | Format-Table -Property Title, Web, DeletedBy, DeletedDate -Autosize -Wrap

    Then to remove each item from the Recycle Bin I used the delete command.

    $site.Recyclebin.Delete($_.ID)

    The full script is shared below. Remember to review, rename and test this script before using it in a production environment.

    One quirk I found while creating the script was that through the web browser, SharePoint reported the time each file was deleted correctly whereas, in PowerShell, the time was not honouring GMT summer time.

    British Summer Time  in SharePoint vs. PowerShell
    British Summer Time in SharePoint vs. PowerShell

    Enjoy and delete carefully!

  • Security trimmed top navigation links

    Security trimmed top navigation links

    I was asked to review a client environment yesterday to find out why the links in their top navigation bar were displaying for users that did not have permission to the particular sites.

    Creating sites

    It turns out that when sites were being created by the client on SharePoint Foundation 2010 they were being created without the ‘include on the top navigation bar’ check box ticked. As a result, the link was then not automatically added to the top navigation bar but instead later manually added and so was not security trimmed link.

    Display this site on the top link bar of the parent site
    Display this site on the top link bar of the parent site

    It was then after removing permissions to the various sites that it became clear that users were able to see the top navigation bar link to the sites even though they did not have access.

    Obviously, there are situations when users don’t have permissions to a site and you don’t want them to see that the site even exists. An example of this might be in an extranet scenario when you have third parties accessing project sites and you don’t want those third parties seeing the names of other project sites that may exist let alone the content…so how do we prevent this?

    Identifying security trimmed links

    By reviewing the URLs of the links in the top navigation bar I was able to identify whether the links were security trimmed or not. If the field for the URL is disabled then the link is security trimmed and most probably created when as the site was created.

    Custom top navigation link that is not security trimmed
    Custom top navigation link that is not security trimmed
    Security trimmed top navigation link
    Security trimmed top navigation link

    Adding new security trimmed links

    After identifying the problem, I then had to make the existing links security trimmed. I did this in two stages. The first was to make a note of the position of the link that needed to be replaced. I then deleted it from the top navigation bar using the ‘Top Link Bar’ site settings page (_layouts/topnav.aspx). The second stage was then to create the new security trimmed link by using the PowerShell code below.

    Modify the $SPWeb and @(“Site Name”, “/sitename/default.aspx”) arguments as required and run the code for each of the top navigation bar links that need to be security trimmed. Remember the old link will need to be removed and the new one ordered as required.

    Conclusion

    It appears SharePoint, specifically SharePoint Foundation 2010 only honours security trimmed links in the top navigation when the links are created automatically as opposed to being created manually.

    Note: this post specifically targets SharePoint 2010 Foundation which does not include the extended navigation that is included as part of the Publishing feature.

  • Hiding an empty rich text column in XSLT

    Hiding an empty rich text column in XSLT

    This morning I was trying to create a new style in the itemstyle.xsl stylesheet to use within the content query web part (CQWP). I needed a custom style to display a list of announcements, some of which had content and others didn’t and this style was to improve this output.

    The problem is that the ‘Body’ column of an announcement or more importantly the ‘rich text’ field type is never really empty. Even when the column genuinely empty and has no rich text content, a hidden HTML element (a div) exists and acts as a wrapper for any content. As a result, if you try and use a typical ‘if equals null’ statement to hide the rich text column it won’t work because of this hidden element.

    Examples

    An empty rich text column on SharePoint 2010 always has 37 characters as shown below.

    Empty rich text column in SharePoint 2010
    Empty rich text column in SharePoint 2010

    With SharePoint 2007 the rich text column has 65 characters when empty, again as shown below.

    Empty rich text column in SharePoint 2007
    Empty rich text column in SharePoint 2007

    Solution

    The solution, in the end, was to use the string-length function to determine if the rich text column was longer than the standard 37 characters on SharePoint 2010 as identified above.

  • Working with Managed Paths in SharePoint using PowerShell

    Quite a common requirement for implementations of SharePoint that I am involved in is to create new managed paths for a given web application.

    While it is a simple task to perform via Central Administration, I inevitably turn to PowerShell to achieve this so that I can then include it as part of larger configuration or deployment scripts.

    Define managed paths in Central Administration
    Define managed paths in Central Administration

    To manage managed paths in SharePoint we use the PowerShell Get-SPManagedPathNew-SPManagedPath and Remove-SPManagedPath cmdlets.

    Reviewing existing managed paths

    To get a list of all the managed paths for a given web application we use the Get-SPManagedPath cmdlet as shown below.

    Get-SPManagedPaths PowerShell Cmdlet
    Get-SPManagedPaths PowerShell Cmdlet

    Creating a new explicit path

    Explicit managed paths only allow one site collection to be created at a specific path. An example of this is the root site collection of a web application which has an explicit managed path of “/” (https://sharepoint.jcallaghan.com).

    To add a new explicit managed path to a web application we use the New-SPManagedPath cmdlet and include the -Explicit parameter.

    Adding a wildcard managed path

    Wildcard managed paths allow one or more site collections to exist at a specified path. This is the same as the default ‘sites’ managed path that we should all be familiar with (https://sharepoint.jcallaghan.com/sites/projectxyz).

    To add a wildcard managed path we run the command as we did for an explicit managed path however we don’t include the -Explicit parameter.

    Custom managed paths added
    Custom managed paths added

    Removing an existing managed path

    There may be times when you need to remove managed paths. This can be done by running the Remove-SPManagedPath cmdlet and specifying the name of the managed path to be removed and what web application to remove it from. When removing a managed path you will be prompted to confirm the removal action – this can be silenced by adding -confirm:$false to the command.

    Conclusion

    Using the SPManagedPath nouns in PowerShell we can get a list of existing managed paths, create explicit or wildcard managed paths and also remove existing managed paths for a given web application.

  • Adding a Yes, No, Cancel prompt to a PowerShell script

    When I write PowerShell scripts, I often want to step through sections. This allows me to decided whether to proceed with parts of my script or not. I achieve this by using the ChoiceDescription class as demonstrated below.

    More information about using prompts in PowerShell can be found in the following article and tip on Microsoft TechNet.

  • Clear the SharePoint Quick Launch using PowerShell

    Clear the SharePoint Quick Launch using PowerShell

    Today I had a requirement to remove all the headings and links from the quick launch navigation of hundreds of SharePoint sites. The sites were being provisioned as part of a PowerShell deployment script that was deleting the default list and libraries. Going through each of these sites manually was not an option – so I edited the deployment script to include a function to remove the headings for me.

    SharePoint Quick Launch with Headings
    SharePoint Quick Launch with Headings

    I remembered doing something similar to this back on SharePoint 2007 but I didn’t have access to the previous script or project and instead had to research the subject for a while to find what I needed.

    Solution

    A post from Get-SPScripts supplied me with what I was after, although it was part of a much larger script. So I picked away at their code and made it into the following PowerShell function to re-use in other projects.

    The above Remove-SPQuickLaunchLinks function will remove all headings and links from the SharePoint quick launch for a particular site.

    Empty SharePoint Quick Launch
    Empty SharePoint Quick Launch

    Remember to review, rename and test this script before using it in a production environment.

  • Backup and download solutions in SharePoint from the configuration database

    Backup and download solutions in SharePoint from the configuration database

    The need to backup or download SharePoint solutions or WSPs from SharePoint come’s up from time to time. This usually crops up for me when upgrading client environments, and they have forgotten where they put their original solutions, or there is a discrepancy as to which version they installed.

    To download the solutions from the config database run the following PowerShell script. This will save all of the solutions stored in SharePoint’s config database to a directory (“C:\Solutions”) on the local machine.

    Remember to review, rename and test this script before using it in a production environment.