Office 365 – Inquisitive M365 https://thomasdaly.net Yet another SharePoint / Office 365 blog Fri, 06 Mar 2026 14:43:42 +0000 en-US hourly 1 116451836 Sort Your SharePoint Site Directory Alphabetically https://thomasdaly.net/2025/02/02/sorting-your-sharepoint-site-directory-by-title-with-pnp-search/ https://thomasdaly.net/2025/02/02/sorting-your-sharepoint-site-directory-by-title-with-pnp-search/#comments Sun, 02 Feb 2025 21:53:07 +0000 https://thomasdaly.net/?p=3339 In my previous article, Build a Site Directory with PnP Search Web Parts, I walked through how to create a dynamic site directory using PnP Modern Search. While that setup provides a powerful and flexible way to display SharePoint sites, you may have noticed a limitation—SharePoint doesn’t allow sorting by the Site Title property out of the box.

This article covers the extra step needed to sort your site directory alphabetically by title. The trick? Leveraging RefinableString fields to make the Site Title sortable. It’s a simple process with just a few tweaks, and by the end of this guide, you’ll have a properly sorted site directory in no time. Let’s dive in.

Updating the Search Schema

Navigate to the SharePoint Admin Center

Expand ‘Advanced’ then click ‘More Features’ and finally click ‘Open’ under the ‘Search’ group

Next click ‘Manage Search Schema’

Next enter ‘RefinableString’ in the search box and click the green button

Hover over any of the available properties and find the drop down, then click ‘Edit Map Property’

It’s not critical on what number refinable string you choose

Scroll all the way to the bottom and click ‘Add a Mapping’

Enter ‘display’ and click ‘Find’, then select Basic:displaytitle and click O

Verify the mapped managed property and click ‘OK’

This completed the Search Schema changes. These can take quite some time to take effect so be patient. It could be a day before you see it working.

Next navigate to the SharePoint page with the PnP Search Web Part configured as a Site Directory

NOTE: The following steps are only necessary if you have customized the Custom template.

  1. Edit the page and open the web part properties of the Search Results web part
  2. On the second page, click on the curly braces { }
  3. Take a copy of the entire

Change the template to Debug. This is so that you can visually see the property to ensure that the search property has taken effect.

Go back to the first page of the web part properties, enter the name of the RefinableString## you modified and hit Enter.

The result should appear on the left hand site – most likely it will read null to begin with. It will eventually populate. It can appear at any time but at least wait 8 hours or a full day. Recheck the steps in the search config but at this point there is not much more to do but wait.

What to do if the site title will no show up?

If it’s just not showing for a few sites

  • Update the Site Title of the site. Change it temporarily and then change it back
  • Trigger a reindex on the 1 site

If it’s not showing up for many sites

Recrawling puts strain on the service as a whole so it’s not meant to be run over and over as it can take weeks to finish on very large sites.

After the crawl property appears move on to the next step.

Apply the Sort

Navigate back to the Search web part page and edit the web part properties

Click ‘Edit sort settings’

Type in your RefinableString## into the Field name box, click Default Sort and then click ‘Add and save’

The sorting is now set to alphabetical order from A-Z by default.

Next go to the second page of the web part properties

Set the template back to what it previously was. In our case Custom and then click on the { } to edit the custom template

Copy / Paste in the template, Save and Republish.

The final result should be in order by site title.

Wrap Up

Sorting your SharePoint site directory by title might not be possible out of the box, but with a little creativity—leveraging RefinableString fields—it becomes a straightforward solution. By following these steps, you can ensure your directory is organized in a way that makes finding sites easier for users.

This small but impactful tweak enhances usability and keeps your directory structured exactly how you need it. If you’re already using PnP Search Web Parts, this is a great optimization to implement. Have questions or run into issues? Drop a comment—I’d love to hear how this worked for you!

]]>
https://thomasdaly.net/2025/02/02/sorting-your-sharepoint-site-directory-by-title-with-pnp-search/feed/ 2 3339
Adding Font Awesome to SPFx React project https://thomasdaly.net/2022/01/20/adding-font-awesome-to-spfx-react-project/ https://thomasdaly.net/2022/01/20/adding-font-awesome-to-spfx-react-project/#comments Thu, 20 Jan 2022 18:39:00 +0000 https://thomasdaly.net/?p=2688 Introduction

I love the FontAwesome library and use it on many projects. I was a huge fan of FontAwesome 4.0 and had a little struggle moving over to using 5.0 + 6.0 in my projects. I was use to the old way of including the link to the CSS files and using the class names. You can still do that if you wish but this article shows using the React component approach. It’s simple once you’ve done it a few times.

Install React Font Awesome

npm i –save @fortawesome/react-fontawesome

Install the Core Packages [required]

npm i --save @fortawesome/fontawesome-svg-core

Install the Icon Packages, use only the ones you need. If you only use solid icons then choose that. If you are not sure right now then you can include them all and remove them later.

npm i --save @fortawesome/free-solid-svg-icons
npm i --save @fortawesome/free-regular-svg-icons
npm i --save @fortawesome/free-brands-svg-icons

Go Find a Fonthttps://fontawesome.com/icons

Keep in mind that you only have the free library so you can only use those unless you buy the pro license

If you pick an icon and it says ‘Start Using this Pro Icon’ then you need to choose something else or purchase a license

You can filter the free ones by clicking this on the left.

I’ve selected the fish icon, next you need to take note of the type ‘Solid’ or ‘Regular’

Now import the FontAwesomeIcon component and the icon you want from the correct package

This fish icon is in the Solid package.

If I wanted to use something from the regular package like this icon

This font is in ‘Regular’ package you would import it from that package instead

There is intellisense inside visual studio to help get the correct name of the icon

Now the result

If you wanted to use the brand icons you would follow the same approach. I believe that all the brands icons are free.

I love FontAwesome library of icons and use them in many projects. The PRO version unlocks so many more fonts and customizations that it’s worth buying if you plan to use them and want to support this project.

]]>
https://thomasdaly.net/2022/01/20/adding-font-awesome-to-spfx-react-project/feed/ 2 2688
Alerts Header – SPFx project https://thomasdaly.net/2021/10/10/alerts-header-spfx-project/ https://thomasdaly.net/2021/10/10/alerts-header-spfx-project/#comments Sun, 10 Oct 2021 21:24:00 +0000 https://thomasdaly.net/?p=2660

Introduction

This new project is a nice little feature that you can easily add to your SharePoint site to enable site messages or alerts. Having the ability to display site alerts is one of the top 10 most requested features to enhance SharePoint. This customization is built as an SPFx application customizer, with a little bit of UI hacking. This project can be used as-is or as a starting point for you to extend and create your own combinations of notifications, icons and colors.

  • Git Repo
  • Pre-Build SPPKG
  • Overview

    The Alerts Header solution contains an SPFx application customizer and a list schema for the alerts messages. The list will automatically be created for you when the app is added to your site. Currently the app displays messages from the site it’s in but could be easily modified to pull notifications from another site. Initially this was designed to be deployed the the entire intranet and activated on each site. Each site had the option to display ‘local’ alerts, alerts contained within the site and remote alerts, alerts contained at the root site collection “/”. The remote alert functionality has been commented out for simplicity in the Git Hub repo but could be reenabled with just a little effort.

    Implementation

    The List

    The solution installs an ‘Alerts’ list in your site. This list contains a number of required fields to properly display the alerts.

    Title – The first part of the text that is displayed

    Description – The second part of the text that is displayed. The description will fill the reminder of the space and truncate with ‘…’

    Alert Type – The icon that is displayed. Warning, Actionable or Info as show below from top to bottom.

    Link – The navigation url and the text that will be displayed

    Start Date / Time – The date and time that the alert will be displayed

    End Date / Time – The date and time that the alert will no longer be displayed

    Items will immediately start to show up once they are added to the list and hit the duration between the start and end date.

    The Web Part

    This view below shows the alerts application customizer with all three types of alerts and active.

    Icons – The icons used are from the Font Awesome 5.0 free library. You could easily update the code to change out the icons.

    Expand / Collapse – If the alert has a long description it will be truncated once it fills the available screen space. The up / down arrow will allow the user to expand and collapse the alert to read the full description.

    Dismiss / Close – The alert has and close icon at the far right to dismiss the alert. The alert will no longer show during that session. A new session will display the alert again. New Tabs are equivalent to new sessions.

    SharePoint UI Hack – I hinted earlier that I’ve included a bonus UI Hack for free, haha. Take a peek at the picture below. The normal location of an application customizer is at the very top of the page. I did not want the alerts to show up there so I opted to move them to the bottom of the header. This is a non supported customization according to Microsoft. I’m pointing this out for transparency.

    Responsive / Mobile Friendly – The web part was designed with the intention of working across all devices and screen sizes. The functionality is somewhat different on the mobile. Once the item is expanded it can only be closed. I’ve do it this way to intentionally close them out to regain the screen real estate back on the small devices.

    Customizations

    The web part comes with a few notification types. You can add more types directly to the list or to the list schema. After you add new Alert Types you would then need to do a few more updates

    IAlerts.types.ts – Add your new Alert Types to the enumeration

    AltertItem.tsx – Add new conditions for the new Alert Types and the icon you want to use

    AlertItem.module.scss – Copy / Paste an existing Alert Type combination and change the colors to meet your needs.

    Conclusion

    Site Alerts are one of the top 10 features requested from clients and customers to build. I’m happy to be able to post this feature to share even though it’s not 100% Microsoft compliant. The tiny UI hack positions this feature in the location where I felt it belongs. I hope and wish that Microsoft would give us more Zones in which we could target in the application customizers.

    ]]>
    https://thomasdaly.net/2021/10/10/alerts-header-spfx-project/feed/ 15 2660
    SharePoint – SPFX Back to Top https://thomasdaly.net/2021/08/14/sharepoint-spfx-back-to-top/ https://thomasdaly.net/2021/08/14/sharepoint-spfx-back-to-top/#comments Sat, 14 Aug 2021 04:35:03 +0000 https://thomasdaly.net/?p=2605 Introduction

    This post will demonstrate another SharePoint UI hack that is typically seen on many modern sites today. Back to top is a button that will position itself on the right hand bottom corner of the screen once you scroll a bit down. It’s very useful on long pages where your users might want to scroll up quickly and not get mouse scroll rash on their fingers in the process. This is another client request that I hear infrequently but I found this as something that the general population can implement and learn from.

    Before you continue, I must mention that SharePoint UI hacks are unsupported and frowned upon by Microsoft as we cannot rely on the UI [html, classes and DOM] to remain the same over time. With that said you are free to implement and learn more about SPFX and the current DOM given this example. When Microsoft implements a change this would potentially stop working and that is all. Post an issue on github and I would be happy to review and make a change.

    Overview

    I’m not going to go into the nitty gritty details of the code in this article, you can look at the code for that. This customization is based on an SPFX application customizer. This application customizer injects a button when the users scrolls past a certain point. The button would then jump the user back to the top of the scroll region.

    The scroll region might change at some point for SharePoint, rendering this customization ineffective.

    Implementation

    In the SPFX application customizer there is some code to detect the scroll event. There is also some code to detect the page navigation event as it’s somewhat tricky. When users navigate away from a page application customizers are not re-rendered. There is also some code to detect a navigation event and refresh the scroll area so that this SharePoint UI hack continues to work.

    The current scroll region is defined in the BackToTop.tsx. The current scroll region that is the target can be seen below.

    The customization is also theme aware and will respect the primary color of the theme.

    Installation

    As with many github solution you can always clone the repo and make any changes you desire to the solution, check out the REPO. Alternatively you can take the latest build and install it into your app catalog.

    Once you install the solution into your app catalog it will be available as an App on your site. It’s as simple as that.

    Summary

    I hope you enjoy this, yet another SharePoint UI Hack. It’s a great little enhancement if you have long content and want to scroll back up to the top of the page quickly. Feel free to use the github code to inspect how it’s implemented, make changes or enhancements or just implement it as the package is available for you. I’ll mention again that SharePoint UI hacks are not supported by Microsoft so don’t ask them for support. If you implement this you should always remove it before troubleshooting other issues. Generally it’s safe to apply as it won’t adjust or change anything permanently within your site, but take note that if there is a change with the SharePoint UI it could stop working as some point in the future. Please feel free to reach out or comment if your feedback or experience.

    ]]>
    https://thomasdaly.net/2021/08/14/sharepoint-spfx-back-to-top/feed/ 6 2605
    Create Site Collection App Catalog https://thomasdaly.net/2021/05/30/create-site-collection-app-catalog/ https://thomasdaly.net/2021/05/30/create-site-collection-app-catalog/#respond Sun, 30 May 2021 03:09:09 +0000 https://thomasdaly.net/?p=2596 I’m posting this quick tip on how to create a site collection based app catalog with PowerShell. I used this over and over when I’m developing and I think it will be helpful for many people to have a quick script to be able to execute.

    I’m using the SharePoint Online Management Shell for this script. You simply need to replace the domain of your tenant and execute.

    #configuration
    $domain = "YOURDOMAIN"; #{domain}.sharepoint.com
    $targetSite = "your site"; #https://{domain}.sharepoint.com/sites/{targetSite}
    #script
    $siteUrl =  "https://$domain.sharepoint.com/sites/$targetSite"
    $adminSiteUrl = "https://$domain-admin.sharepoint.com"
    Connect-SPOService -url $adminSiteUrl
    $site = Get-SPOSite $siteUrl
    Add-SPOSiteCollectionAppCatalog -Site $site

    Enjoy!

    ]]>
    https://thomasdaly.net/2021/05/30/create-site-collection-app-catalog/feed/ 0 2596
    PnP PowerShell V4 Provisioning with Azure Functions V3 and Power Automate / Flow https://thomasdaly.net/2021/04/09/pnp-powershell-v4-provisioning-with-azure-functions-v3-and-power-automate/ https://thomasdaly.net/2021/04/09/pnp-powershell-v4-provisioning-with-azure-functions-v3-and-power-automate/#respond Fri, 09 Apr 2021 17:56:16 +0000 https://thomasdaly.net/?p=2357 Introduction

    Trying out the new way to Provision via PnP PowerShell V4 and Azure Functions V3 was a little challenging at first. There are some of the difference and issues that I encountered which I could not get to work correctly. I happen to be one of the early ones that need to do this because Azure had made a change to their interface which prevented using PnP PowerShell V3 and the Microsoft documentation was not updated. Since that time and now Microsoft has updated their online documentation with examples of the exact steps. I’ve decided to post the process I used to get everything working but I still suggest reading the official Microsoft article as well.

    Install PnP.PowerShell

    1. Open PowerShell 7 (if not installed, please install from here – https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-windows?view=powershell-7.1)
    2. Run the following cmdlet to install the PnP.PowerShell module

    Install-Module -Name "PnP.PowerShell" -RequiredVersion "1.5.0"

    Set up app-only access to your tenant

    1. Open PowerShell 7 and run the following cmdlets to register an Azure App

    Select the appropriate tenant name before running.

    Register-PnPAzureADApp -ApplicationName "PnPProvisioningDemo" -Tenant "<tenant-name>.onmicrosoft.com" -DeviceLogin -OutPath .

    Follow the instructions to complete the Azure AD App registration. You will need to open a browser and enter the url above and supply the code. You will also be asked to log in.

    This will now take 60 seconds to create the certificate in the out path that was specified in the above command

    • After the cmdlet is executed. It will create a PFX file. Keep this file saved, as we will have to upload it to Azure function.
    • Login to https://aad.portal.azure.com/
    • Click -> Azure Active Directory -> App Registration
    • Search for ‘PnPProvisioningDemo’ and the select the App Registration which got created. Copy the Application (client) ID from here.
    • Click API Permission
    • Click API Permissions, and Grant admin consent for {tenant}
    • In the small popup click Yes to approve this app those permissions.

    Create the Azure function

    • Type in ‘Function’ and select ‘Function App’
    • In the Project Details, click on Create New, for the Resource Group
    • Type in ‘PnPProvisioningDemo’ for the resource group name, click OK

    • Enter in a Function App name – this will need to be unique, Runtime Stack, Version and select a Region in which you want the resources inr its created, navigate to your new function App
    • From the dropdown menu, select host.json file and add the following entry and save it.
    • Click ‘Review + create’
    • Click ‘Create’

    After a few moments all of the assets will be created.

    • Click ‘Go To Resource’
    • Next click on ‘App Files’
    • I typically bump the timeout to 10 mins for the Azure Function from the default 5 minutes.
    • In the App Files > host.json, add this line. Make sure to use a ‘,’ on the previous line
    "functionTimeout": "00:10:00"
    • Click Save to lock in the changes
    • Click on the dropdown and select requirements.psd1
    • You can remove the ‘Az’ = ‘5.*’ as it won’t be used
    • Click Save to lock in the changes
    • Create a new Azure Function Functions > Add:
    • Create a new HTTP Trigger function, by selecting the options as shown below
    • Scroll down, name the function InvokePnPSiteTemplate
    • Set Authorization level, Anonymous
    • Click Save

    Upload the code for your Azure Function

    • Go to the Function App main screen and select Advanced Tools
    • Click “Go”, this will open Kudu
    • Select “PowerShell” from the Debug Console menu at the top.
    • Navigate to site\wwwroot\InvokePnPSiteTemplate

    This is where it gets complicated. Hold on to your hats.

    Extract the AzureFunCode.zip file provided as a sample.

    • Add your PnP template into the pnp-template.xml file
    • Update run.ps1
      • set $filePath to the correct drive [C or D] that you see in the azure portal
    • set $AzAppId to match your Azure App Id
      • set $certFileName to match your exported certificate
      • set $tenant to match your tenant prefix
    • Drag & drop the files to the site\wwwroot\InvokePnPSiteTemplate directory

    NOTE: If you want to use a different version of PnP PowerShell then you can generate the PnP.PowerShell files for upload from the command line. Use the folder that is exported to upload.

    Create a new folder and then execute the following commands

    Save-Module PnP.PowerShell . 
    1. After the code is updated. Navigate back to Function screen on Azure portal and click on ‘Get Function Url’ and copy the URL

    Create the Flow

    1. Go to the Power Automate site, sign in, and choose Create -> Automated Cloud Flow
    2. Click Skip at this screen
    1. Click ‘untitled’ and give the Flow a name, PnPProvisioningDemo
    1. Search for Request, and select Request – When a HTTP Request is received.
    1. Enter the following JSON as your request body:
    {
        "type": "object",
        "properties": {
            "webUrl": {
                "type": "string"
            },
            "parameters": {
                "type": "object",
                "properties": {
                    "event": {
                        "type": "string"
                    },
                    "product": {
                        "type": "string"
                    }
                }
            }
        }
    }
    • Select + New Step and choose Add an action.
    • In the search box type ‘http’ and click HTTP
    • Configure the properties as shown below.
    • You also need the Azure Function, HTTP endpoint. This is in the Azure Function, Overview screen, click Get Function Url
    • Copy this http endpoint to then paste into the flow
    • Choose Save Flow. This will generate the URL that you will copy in the next step.
    • Back in the Flow, Paste the URI in this box
    • Save the Flow
    • Once save is completed, click back on the first step and copy the Flow HTTP Post Url. Save this for later

    Create the Site Design

    In this section we are going to create a SharePoint Site Design, based on a Team Site template.

    1. Open SharePoint Online Management Shell and connect to your tenant using Connect-SPOService
    Connect-SPOService -Url https://[yourtenant]-admin.sharepoint.com
    • Update the URL property in the code below. Set the url property to the value you copied when you created the flow
    {
      "$schema": "schema.json",
      "actions": [
       {
          "verb": "triggerFlow",
          "url": "{UPDATE THE FLOW URL HERE}",
          "name": "Apply PnP Template",
          "parameters": {}
        }
      ],
      "bindata": {},
      "version": 1
    }
    
    • Select the JSON again and copy it, this will put it on your ‘clipboard
    • Open PowerShell and enter the following to copy the script into a variable and create the site script:
    $script = Get-Clipboard -Raw
    #before the next step verify you have data in $script by typing this
    $script
    #if that is empty try the copy and run the first line again.
    Add-SPOSiteScript -Title "Contoso Team Config" -Content $script
    Get-SPOSiteScript
    • You will see a list of one or more site scripts, including the site script you just created. Select the ID of the site script that you created and copy it to the clipboard.
    • Use the following command to create the site design:
    Add-SPOSiteDesign -Title "Contoso Team" -SiteScripts {Paste the ID of the Site Script here} -WebTemplate "64"

    Conclusion

    That completes all the necessary steps to get the basics up and running. Hope this was helpful walkthrough to make your first time getting started a little easier. The screens are constantly changing in Office 365 but the basics steps should remain the same. Leave a comment below if you get stuck or have a suggestion.

    ]]>
    https://thomasdaly.net/2021/04/09/pnp-powershell-v4-provisioning-with-azure-functions-v3-and-power-automate/feed/ 0 2357
    PnP PowerShell V4 and Azure Functions V3 – First Look https://thomasdaly.net/2020/12/19/pnp-powershell-v4-and-azure-functions-v3-first-look/ https://thomasdaly.net/2020/12/19/pnp-powershell-v4-and-azure-functions-v3-first-look/#respond Sat, 19 Dec 2020 02:06:59 +0000 https://thomasdaly.net/?p=2258 I’m writing this blog to share my experiences with working the latest version of PnP PowerShell V4 this .NET Core version and Azure Functions V3.

    Background

    Up until a few weeks ago I was able to write PnP PowerShell provisioning code and run it in an Azure Function V1 to do almost anything I wanted in my SharePoint site. I was receiving warnings that Azure was going to be deprecating the V1 functions but not pay much attention. This forced me to try using the new way of using the Azure V3 function which run PowerShell .NET Core and the new PnP PowerShell libraries built on .NET Core.

    Azure has removed all ways through the UI to use experiment languages. You can set the PowerShell Azure function to run in V1 mode, however the toggle switch to enable experimental languages has been removed. Rendering this article is no longer valid – https://docs.microsoft.com/en-us/sharepoint/dev/declarative-customization/site-design-pnp-provisioning#create-the-azure-function [UPDATE: 11/25/2020 – Microsoft has updated that article]

    The now older version of PnP PowerShell is referred to as PnP PowerShell V3 which is built on the CSOM for .NET 4.5 Libraries. This runs in PowerShell up V5

    The newer PnP PowerShell is referred to as PnP PowerShell V4 which is built on the CSOM for .NET Standard 2.0. This run in PowerShell V7.

    You can read more about this here – https://github.com/pnp/PnP-PowerShell. A tremendous amount of work has been done to make the libraries available for .NET Core. It’s time we all start using the new V4 version and help the project by submitting defects and contributing.

    My Experience

    I was faced with many issues which I will outline. While this was my first time experimenting with the new PnP PowerShell and the Azure Functions V3 it was not an easy one. I wrote this article before changes to official Microsoft documentations updates and had to learn by trial and error which can be tedious.

    1. On Demand Loading – the function now use a requirements.ps1 file that contains a manifest of the libraries that your function will using. This is how you load the PnP PowerShell libraries. The files will be downloaded on demand when your function is initialized and will stay in memory until your function goes to sleep.
    2. Problems Connecting / Authentication – I believe there is some issues with Authentication as I spent many hours trying various combinations to work. I would recommend using a certificate as that is the only way I was able to get the functions to work correctly. I think it’s possible some pieces of PnP PowerShell V4 are not working 100% in Azure Functions. I was using 0.2.15-nightly
    3. Different Commands – not a huge deal but you need to be aware that some of the commandlets have changed and should update them accordingly. It’s well documented just be aware.
    4. Pre-Release Version – the PnP PowerShell is still currently pre-release and scheduled to be GA in Jan 2021. We can’t do anything about this, if you need this functionality you will be using the pre-release version. It shouldn’t matter too much if the functionality works with the version of PnP PowerShell that you built it on then you are fine. Somethings may or may not work – always send feedback in the github repo’s for issues.
    5. Timeouts – the on demand loading seemed to timeout my functions [default 5 mins max on Azure Function runtime] this was a problem with the libraries taking 3-4 minutes to load and my code only had 1 minute to finish.

    In the host.json file I had increased the timeout to 10 minutes to account for this. At the time of this article I have not tried to upload and host the libraries in my function [more on this in another article]

    updating host.json file

    Summary

    Since writing this article I have made more progress in this area. I will post a follow up in the next week with a walk through of setting up a site script to kick off a Power Automate Flow, that runs the Azure Function to make some modifications to a SharePoint site. I’ve had a lot of fun playing around and experimenting with different flows and can now appreciate this method along with the power behind it. I am excited to see the progress here and looking forward to future updates from the PnP PowerShell team!

    ]]>
    https://thomasdaly.net/2020/12/19/pnp-powershell-v4-and-azure-functions-v3-first-look/feed/ 0 2258
    Remove Left Navigation on Modern Team Sites https://thomasdaly.net/2020/11/12/remove-left-navigation-on-modern-team-sites/ https://thomasdaly.net/2020/11/12/remove-left-navigation-on-modern-team-sites/#respond Thu, 12 Nov 2020 13:46:55 +0000 https://thomasdaly.net/?p=2188 I am frequently asked how do I removed the left navigation on the modern Team sites? Most people don’t know this but if you just remove all the items from the navigation it will go away on it’s own!

    The draw back is that it’s removed for the whole site. Maybe you just want it gone on one page. Well in that case you’ll have to use CSS to do that. For the one page approach I would add a Modern Script editor to the page.

    Immediately after removing the left navigation, depending on your screen size, you will notice that the screen does not go full width. This would also need to be fixed with injecting CSS either through the page or into the entire site via Application Customizer.

    not quite full screen

    Microsoft doesn’t recommend modifying their CSS but they almost give us no choice. For now I’ll leave you with the CSS that works (for now) to update these zones. You’ll need to use one of the two methods above to implement this on Modern pages.

    // Trimming Left Navigation
    #spLeftNav { 
     display: none;
    }
    
    // Full Screen CSS
    .CanvasComponent.LCS .CanvasZone {
     //there is a max width on this set to 1268px, we'll make it 100%
     max-width: 100%;
    }

    ]]>
    https://thomasdaly.net/2020/11/12/remove-left-navigation-on-modern-team-sites/feed/ 0 2188
    Using JSOM to update Taxonomy Fields in SPFx https://thomasdaly.net/2020/10/29/using-jsom-to-update-taxonomy-fields-in-spfx/ https://thomasdaly.net/2020/10/29/using-jsom-to-update-taxonomy-fields-in-spfx/#respond Thu, 29 Oct 2020 08:00:37 +0000 https://thomasdaly.net/?p=2119 Introduction

    Last week I was working on a project that involved setting some managed meta data fields, both the single value and multi values fields. I typically use PnP-JS to streamline the calls to SharePoint however I found there are issues with PNP-Js and managed meta data fields. It’s been said that MMD fields are unable to be set with REST calls (PnP-Js uses REST). There are mixed reviews out there regarding whether it works or not. For me it did not work. My particular scenario involved update 6 single value fields and 1 multi value fields at the same time. While investigating I reach out to a friend Beau Cameron to verify my code as he wrote 2 great articles for single value fields and multi value fields. The results were to flaky using REST so the decision was made to use JSOM. In this article I will share the end code as there were a few things I want to document for future reference.

    Using JSOM in SPFx

    Before you can start writing the code you must first get your project setup in order to work with the JSOM libraries.

    It’s a good idea to read up on the official guidance as this method follows it but I prefer to load the libraries differently. The Microsoft article loads the libraries in the configuration as external resource but you must specific the full url of your site and I prefer not to lock that value of the site url into a configuration file that would prevent me from using this in another tenant without rebuilding.

    Install the Typings

    install project dependencies

    npm install @types/microsoft-ajax @types/sharepoint --save-dev
    

    In the code editor, open the ./tsconfig.json file, and in the types property, after the webpack-env entry, add references to microsoft-ajax and sharepoint

    {
      "compilerOptions": {
        // ...
        "types": [
          "es6-promise",
          "es6-collections",
          "webpack-env",
          "microsoft-ajax",
          "sharepoint"
        ]
      }
    }

    Register the Scripts

    In the file you wish to use the JSOM library, require the scripts.

    require('sp-init'); 
    require('microsoft-ajax'); 
    require('sp-runtime'); 
    require('sharepoint');

    Loading the JSOM libraries

    In the previous section I mentioned that I prefer to dynamically load the libraries to avoid hard coding the site url into the configuration files. In the code below is how I am loading the libraries dynamically.

    Note: In the state I have 2 values

    1. loadingScripts: boolean, intially set to true
    2. Errors: any, initially set to []

    This way you can wait until the scripts are loading to rendering fields that require them and I’m also collecting the errors for later consumption.

    public componentDidMount() {
        this.loadScripts();
      }
    
    
      public getSiteCollectionUrl(): string {
        let baseUrl = window.location.protocol + "//" + window.location.host;
        const pathname = window.location.pathname;
        const siteCollectionDetector = "/sites/";
        if (pathname.indexOf(siteCollectionDetector) >= 0) {
          baseUrl += pathname.substring(
            0,
            pathname.indexOf("/", siteCollectionDetector.length)
          );
        }
        return baseUrl;
      }
    
      private loadScripts() {
        const siteColUrl = this.getSiteCollectionUrl();
        try {
          SPComponentLoader.loadScript(siteColUrl + "/_layouts/15/init.js", {
            globalExportsName: "$_global_init",
          })
            .then(
              (): Promise<{}> => {
                return SPComponentLoader.loadScript(
                  siteColUrl + "/_layouts/15/MicrosoftAjax.js",
                  {
                    globalExportsName: "Sys",
                  }
                );
              }
            )
            .then(
              (): Promise<{}> => {
                return SPComponentLoader.loadScript(
                  siteColUrl + "/_layouts/15/SP.Runtime.js",
                  {
                    globalExportsName: "SP",
                  }
                );
              }
            )
            .then(
              (): Promise<{}> => {
                return SPComponentLoader.loadScript(
                  siteColUrl + "/_layouts/15/SP.js",
                  {
                    globalExportsName: "SP",
                  }
                );
              }
            )
            .then(
              (): Promise<{}> => {
                return SPComponentLoader.loadScript(
                  siteColUrl + "/_layouts/15/SP.taxonomy.js",
                  {
                    globalExportsName: "SP",
                  }
                );
              }
            )
            .then((): void => {
              this.setState({ loadingScripts: false });
            })
            .catch((reason: any) => {
              this.setState({
                loadingScripts: false,
                errors: [...this.state.errors, reason],
              });
            });
        } catch (error) {
          this.setState({
            loadingScripts: false,
            errors: [...this.state.errors, error],
          });
        }
      }
    

    State values & Taxonomy Controls

    To work with the taxonomy fields I will be using the Taxonomy Picker from the PnP React Controls

                  <TaxonomyPicker
                    allowMultipleSelections={false}
                    termsetNameOrID="By Whom"
                    label="By Whom"
                    values={this.state.byWhom}
                    context={this.props.context}
                    onValueChanged={(pickerTerms) => {
                      this.setState({ byWhom: pickerTerms });
                    }}
                    loading={this.state.loading}
                  />

    Setting up the JSOM call

    This function I I am preparing the JSOM context getting the list and item and will be updating a number of MMD fields. My state values for the MMD are IPickerTerms from PnP React Controls.

    Warning: You can see I have a function called getItem that will retrieve the list item using CAML query. I must do this in order to get a ‘fresh’ version of the item. If you use the JSOM getItemById you will receive a cached copy and performing an update on it will throw an error that it is unable to make a change.  I also read that you can avoid that error by calling executeQueryAsync right after getItemById so it’s ‘fresh’ but I went this way.

    private updateItemMetaData = async (siteUrl: string, listName: string, itemId: number) => {
        console.log(siteUrl, listName, itemId);
        return new Promise(async (resolve, reject) => {
          let context = new SP.ClientContext(siteUrl);
          let list = context.get_web().get_lists().getByTitle(listName);
          console.log("ctx list", list);
          let item = await this.getItem(context, list, itemId); 
          console.log("ctx item", item);
          this.setTaxFieldProperty(context, item, list, "ByWhom", this.state.byWhom, true);
          this.setTaxFieldProperty(context, item, list, "SubCategory", this.state.subCategory, true);
          this.setTaxFieldProperty(context, item, list, "DocumentStatus", this.state.documentStatus, true);
          this.setTaxFieldProperty(context, item, list, "SubmittalAction", this.state.submittalAction, true);
          this.setTaxFieldProperty(context, item, list, "Phase", this.state.gaPhase, true);
          this.setTaxFieldProperty(context, item, list, "Agencies", this.state.agencies, true);
          this.setTaxFieldProperty(context, item, list, "Trades", this.state.trades, false);
          item.set_item("ActionDate", this.state.actionDate);
          item.update();
          context.load(item);
          context.executeQueryAsync(
            () => {
              console.log("success", item.get_id());
              resolve();
            },
            (sender, args) => {
              console.log("fail", args.get_message());
              resolve();
            }
          );
        });
      };
    
     private getItem = async (context: SP.ClientContext, list: SP.List, itemId: number): Promise<SP.ListItem> => {
        return new Promise((resolve, reject) => {
          const query = `<View Scope='Recursive'><Query><Where><Eq><FieldRef Name=\"ID\" /><Value Type=\"Integer\">${itemId}</Value></Eq></Where></Query></View>`;
          let camlQuery = new SP.CamlQuery();
          camlQuery.set_viewXml(query);
          let allItems = list.getItems(camlQuery);
          context.load(allItems, 'Include(Id)');
          context.executeQueryAsync(
            () => {
              console.log("success", allItems);
              resolve(allItems.get_item(0));
            },
            (sender, args) => {
              console.log("fail", args.get_message());
              resolve();
            }
          );
        });
      }
    

    The Update Function

    In this section we will get the field from the list and update it correctly if it’s a single value or a multi value. Each type must be handled differently. For single value we can set the term as an object but for the multi value we must create a string of the terms. Creating the string of terms is done in the getTermString function.

    private setTaxFieldProperty = (
        context: SP.ClientContext,
        item: SP.ListItem,
        list: SP.List,
        fieldName: string,
        fieldValues: IPickerTerms,
        isSingle: boolean
      ) => {
        let field = list.get_fields().getByInternalNameOrTitle(fieldName);
        let taxField = context.castTo(field, SP.Taxonomy.TaxonomyField) as SP.Taxonomy.TaxonomyField;
        if (isSingle) {
          if (fieldValues && fieldValues.length >= 1) {
            let termValue = new SP.Taxonomy.TaxonomyFieldValue();
            termValue.set_label(fieldValues[0].name);
            termValue.set_termGuid(new SP.Guid(fieldValues[0].key));
            termValue.set_wssId(-1);
            taxField.setFieldValueByValue(item, termValue);
          }
          else {
            taxField.validateSetValue(item, null);
          }
        }
        else {
          if(fieldValues && fieldValues.length >= 1) {
            let termString = this.getTermString(fieldValues);
            console.log(termString);
            let termValueCollection = new SP.Taxonomy.TaxonomyFieldValueCollection(context, termString, taxField);
            taxField.setFieldValueByValueCollection(item, termValueCollection);
          }
          else {
            taxField.validateSetValue(item, null);
          }
        }
      };
    
      private getTermString(pickerTerms) {
        let termString = "";
        pickerTerms.forEach((term) => {
          termString += `-1;#${term["name"]}|${term["key"]};#`;
        });
        return termString.slice(0, -2);
      }

    Summary

    This proof of concept was somewhat challenging to work with the taxonomy fields. So much so that I had to blog it at least for my reference on how I dealt with it. The code above is not meant to be a full solution just a snippet of the key pieces to reliable working with the taxonomy fields. If you are interested in the full look at the code you can check it out here, https://github.com/tom-daly/demos/tree/master/docprocessor. It goes without saying that it’s test/demo code.

    ]]>
    https://thomasdaly.net/2020/10/29/using-jsom-to-update-taxonomy-fields-in-spfx/feed/ 0 2119
    Hub Site Guide https://thomasdaly.net/2020/10/29/hub-site-guide/ https://thomasdaly.net/2020/10/29/hub-site-guide/#respond Thu, 29 Oct 2020 00:16:47 +0000 https://thomasdaly.net/?p=2113 Introduction

    Over the past few months I’ve been involved in many discussions about SharePoint Hub Sites. What I’ve found is that majority of the people involved in the discussions were confused about what SharePoint Hub Sites are, what they do and what they do not do. In this article I will explain a little more about Hub Sites, what you get and call out specifically what you will not get.

    Site Architecture

    Before we begin, it is important to understand the structure of sites in SharePoint. The terminology can be somewhat confusing as it can mean something different given a different context. Since this article is all about modern sites I will use site and site collection to mean the same thing.

    All modern sites are site collections, in SharePoint. This means that each has its own set of features, security, theme, site columns, content types, etc.. By hubbing those sites, they still remain their own entity but with connections between. The following sections will call out the pro’s & cons along with some of the common misconceptions.

    Search

    By default: Searching has two levels of scoping, within the current site and within the tenant. Tenant level searches everything you have access to.

    SharePoint 
X Exit search 
robot 
tenant' 
site 
Organization > SideNavDemo 
All 
Files 
Sites 
News 
We found O results for robot. 
Try looking elsewhere 
p Search all content for robot

    After hubbing: Searching now has three levels of scoping, within the current site, within the hub, within the tenant. Searching within the hub level will find documents in any of the hubbed sites that you have access to.

    SharePoint 
X Exit search 
tenant 
p robot 
hub, 
site 
Organization > Departments > Marketing 
Files 
Sites 
News 
Y Filters 
iconfinder_robot_emoticons_smiley_4927752 
Marketing > Shared Documents 
You modified 3 minutes ago

    Navigation

    By default:  Each site has it’s own navigation. Communication site have a top navigation and Team sites have a left navigation.

    After hubbing: A new navigation is added above the header but below the suite bar. This is the hub site navigation. All sites that are hubbed will receive this navigation. This creates a consistent top navigation across all sites within the hub.

    SharePoint 
- Home Marketing 
Marketing 
Home Documents v 
p Sear 
Site contents 
Edit

    The new navigation is just a collection of link that you must adjust manually. Newly created sites will not be auto added to the hub navigation and the navigation is not security trimmed, however it can be audience targeted [Releasing April 2020].

    Common Misunderstandings:

    • The hub navigation displays all the sites in the hub – No the navigation is a simple list of links that you manually have to maintain.
    • The hub navigation is security trimmed – No the navigation is not security trimmed, however sites are still security trimmed so you can see the links but you would be blocked from the site if you do not have access.

    Security

    By default: Each site has it’s own security and permissions. As an admin you need to grant access for users and groups to each site individually. It could be possible to have access to the hub and not a hubbed site.

    After hubbing: Nothing changes. Security still must be granted to each site individually.

    Common Misunderstandings:

    • Permissions from the hub site rolls down into the hubbed sites – No each site within the hub has it’s own security. Permissions applied to the hub do not apply to the hubbed sites.

    Branding

    By default: Each site can has their own branding. To be crystal clear, in modern sites, branding equals:

    • Theme – General colors
    • Header – Layout [Standard or Compact] and Background
    • Navigation – Menu Style [Mega menu or Cascading]
    • Hub Navigation Logo – the logo located in the hub navigation

    After hubbing: Each site within the hub will receive the all of the items from the above list.

    Common Misunderstandings:

    • All branding customization will be applied to sites within the hub – No only theme colors will applied to sites within the hub. Branding customizations can be applied via features and no features are automatically applied by hubbing a site. This means that any application customizer that you built to adjust the branding further or inject functionality will not be automatically roll down into a hub site. Each hub site maintains its own collection of features and you must activate/deactivate these individually
    • Hubbing does not inherit the footer or site logo – I can’t believe it doesn’t do this hopefully it will in the future.

    Content Rollup

    By default: Sites do not share information with each other directly. You could use a search based web part to find and display a particular type of content across all sites within your tenant.

    After hubbing: The Highlight Content web part can be used to aggregate various content from within the hub. News Articles and Events can also be aggregated up to the hub site.

    Common Misunderstandings:

    • You still must have access to this content within the hubbed site it came from, otherwise you wouldn’t see it.

    General

    Below are a list of a few other considerations that important to know:

    • Hub site do not share site columns or content types
    • You cannot hub a site to another hub site

    Summary

    I think hub sites bring some much needed functionality to logically organizing a bunch of disparate sites together to form an intranet. In my opinion this is where modern SharePoint sites are failing. We need to be able to organize them together to be able to travel between and across them as well as locate information in each. This is exactly what hub sites solve. I hope this article shed some light on some of the edges. Hub wisely my friends!

    ]]>
    https://thomasdaly.net/2020/10/29/hub-site-guide/feed/ 0 2113