モダン リストとライブラリのエクスペリエンスをオプト アウトするOpting out of the modern list and library experience

ユーザーエクスペリエンスの徹底的な見直しの際に拡張性、アクセシビリティ、および応答性の高いデザインの向上を図り、2016 年に SharePoint の ”最新の” エクスペリエンスを導入しました。In 2016, we introduced a new “modern” experience for SharePoint, bringing extensibility, accessibility, and responsive design to a complete overhaul of the user experience. それ以来、従来のモードも引き続きサポートされ利用可能ですが、当時最新であったものが SharePoint および OneDrive の革新の基盤となってきました。Since then, modern has been the basis for innovation throughout SharePoint and OneDrive, although classic mode remains supported and available. リストとライブラリの大部分は最新のエクスペリエンスで問題なく動作する一方、予測どおりに動作しない、または最新のエクスペリエンスで使用すると機能が欠落するリストもあります。The majority of lists and libraries work perfect in the modern experience, but there are also lists which don't work as expected or miss functionality when used in modern. 多くの場合これはリスト (例: JSLink)、またはリスト形式のページ (例: 複数の web パーツの使用) のカスタマイズが原因です。Often this is due to customizations on the list (e.g. JSLink) or on the list form page (e.g. multiple web parts used). SharePoint では、最も互換性のないカスタマイズが検出され、影響を受けるリストとライブラリが従来のエクスペリエンスを使用して表示されます。一方、サイト内の他のリストとライブラリは最新のエクスペリエンスで表示されます。SharePoint will detect most incompatible customizations and will show the impacted lists and libraries using the classic experience, while the other lists and libraries in the site will be shown using the modern experience. 最新のエクスペリエンスと従来のエクスペリエンスを切り替えることができない場合、最新のエクスペリエンスからリストとライブラリをオプト アウトすることもできます。If switching between the modern and classic experiences is not acceptable, then you do have the option to opt out lists and libraries from the modern experience. この記事では、利用できるオプト アウトのオプション、最新のエクスペリエンスからオプト アウトするとよいリストとライブラリの検出方法、およびオプト アウトの最善の方法を説明します。In this article you'll learn about the available opt out options, how to detect lists and libraries that could benefit from opting out of modern and finally how to best handle the opt out.

最新のエクスペリエンスからリストやライブラリをオプト アウトするオプションOptions to opt out lists and libraries from the modern experience

サイト コレクション レベルでオプト アウトするOpting out at site collection level

ID E3540C7D-6BEA-403C-A224-1A12EAFEE4C4 の機能を有効にすると、"最新の" エクスペリエンスを使用してサイト コレクションをオプト アウトできます。You can opt out a site collection from using the "modern" experience by enabling a feature with ID E3540C7D-6BEA-403C-A224-1A12EAFEE4C4. 次の PnP PowerShell を使用して、必要な機能を有効/無効にします。Use the following PnP PowerShell to enable/disable the needed feature:

# Connect to a site
$cred = Get-Credential
Connect-PnPOnline -Url https://[tenant].sharepoint.com/sites/siteurl -Credentials $cred

# Opt out from modern lists and libraries at site collection level
Enable-PnPFeature -Identity E3540C7D-6BEA-403C-A224-1A12EAFEE4C4 -Scope Site
# And again enable modern lists and libraries at site collection level
#Disable-PnPFeature -Identity E3540C7D-6BEA-403C-A224-1A12EAFEE4C4 -Scope Site

Web レベルでオプト アウトするOpting out at web level

ID 52E14B6F-B1BB-4969-B89B-C4FAA56745EF の機能を有効にすると、"最新の" エクスペリエンスを使用して web をオプト アウトできます。You can opt out a web from using the "modern" experience by enabling a feature with ID 52E14B6F-B1BB-4969-B89B-C4FAA56745EF. 次の PnP PowerShell を使用して、必要な機能を有効/無効にします。Use the following PnP PowerShell to enable/disable the needed feature:

# Connect to a site
$cred = Get-Credential
Connect-PnPOnline -Url https://[tenant].sharepoint.com/sites/siteurl -Credentials $cred

# Opt out from modern lists and libraries at web level
Enable-PnPFeature -Identity 52E14B6F-B1BB-4969-B89B-C4FAA56745EF  -Scope Web
# And again enable modern lists and libraries at web level
#Disable-PnPFeature -Identity 52E14B6F-B1BB-4969-B89B-C4FAA56745EF  -Scope Web

リスト レベルでオプト アウトするOpting out at list level

ライブラリ レベルでエクスペリエンスを制御するには、[リストの設定] > [詳細設定] の順に移動して、動作を変更できます。To control the experience at the library level, you can go to List settings > Advanced settings, and change the behavior.

リスト エクスペリエンス構成

次のスニペットに示すように、PnP PowerShell を使用して同じ操作を実行することもできます。The same can also be done by using PnP PowerShell as shown in this snippet:

# Connect to a site
$cred = Get-Credential
Connect-PnPOnline -Url https://[tenant].sharepoint.com/sites/siteurl -Credentials $cred

# Get the list to update
$list = Get-PnPList -Identity "Shared Documents" -Includes ListExperienceOptions

# Set the list experience (0 = Auto, 1 = modern, 2 = classic)
$list.ListExperienceOptions = 2
$list.Update()
Invoke-PnPQuery

注意

  • ライブラリ レベルで設定すると、Web またはサイトコレクション レベルで設定が上書きされます。The settings at the library level override the settings at the web or site collection level.

最新のエクスペリエンスからオプト アウトされる候補となるリストとライブラリの検出方法How to detect lists and libraries that are candidates for being opted out of the modern experience

記事の概要で説明したように、最新のエクスペリエンスからのオプト アウトが必要になるのは特定の場合のみです。Opting out of the modern experience is only needed in certain cases, as described in this article's introduction. 前の章ではオプト アウトの実行方法を説明しましたが、最新のエクスペリエンスからオプト アウトされる候補となるリストとライブラリはどのように見分けられるのでしょうか。Previous chapter showed you how to perform an opt out, but how do know which lists and libraries are candidates to be opted out from the modern experience?

SharePoint モダン化スキャナーで必要な回答を得ることができます。"フル スキャン" モードまたは "モダン リストのエクスペリエンスの準備" モードでスキャナーを実行した場合、スキャナーはあなたのリストについてすべてのデータを収集します。The SharePoint Modernization scanner will give you the needed answers: if you run the scanner in "Full scan" or in "Modern list experience readiness" mode the scanner will collect all the data about your lists. 生成されたモダン UI リストの準備の Excel レポートを使用して、カスタマイズを含むリストを見つけることができます。これはスキャナー データの分析と使用の記事で説明されています。Using the generated Modern UI List Readiness Excel report you can find the lists having customizations as explained in the Analyze and use the scanner data article.

SharePoint の従来のフォールバック メカニズムにより、見つかったリストは従来のエクスペリエンスで表示されます。The found lists will already present themselves in classic, due to SharePoint's classic fallback mechanism. ただし、完全に従来のユーザー エクスペリエンスを提供する場合は、モダン リストとライブラリからサイト コレクション全体をオプト アウトする可能性もあります。If you however want to offer a full classic user experience, than you might want to opt out the complete site collection from modern lists and libraries. これを簡単に行うために、スキャナーはオプト アウトされる可能性がある 1 つ以上のリストを含むすべてのサイト コレクションを一覧表示する、SitesWithCustomizations.csv という名前の CSV ファイルを生成します。To make that easy, the scanner generates a CSV file named SitesWithCustomizations.csv listing all site collections that contain one or more lists that could be opted out.

以下の例が示すように、この CSV ファイルはヘッダーがないサイト コレクションの URL のシンプルなリストです。This CSV file is a simple list of site collection URL's without a header as shown in below sample:

"https://contoso.sharepoint.com/sites/siteA"
"https://contoso.sharepoint.com/sites/siteB"
"https://contoso.sharepoint.com/sites/siteC"

選択したサイト コレクションのオプト アウトの最善の方法How to best handle the opt out of the selected site collections

モダン リストとライブラリからリスト、Web、サイト コレクションをオプト アウトする、上述のオプションを使用できます。You can use the earlier described options to opt out a list, web or site collection from modern lists and libraries. スキャナーを使用して最新のエクスペリエンスからオプト アウトされる候補となるサイト コレクションのリストを生成する場合、PnP PowerShell スクリプトを使用して [一括] を実行できます。If you've used the scanner to generate a list of site collections that are candidates for being opted out of modern, you then can use below PnP PowerShell script to perform a "bulk" opt out.

<#
.SYNOPSIS
Enables or disables the modern list and library experience at site collection level. The script can handle a single site collection or a list of site collections provided via a CSV file. 

To get the CSV file you can run the Modernization Scanner, version 2.3 or higher, and use the "Lists and Library" mode (see https://aka.ms/sppnp-modernizationscanner) or alternatively 
create the file yourselves:

"https://contoso.sharepoint.com/sites/siteA"
"https://contoso.sharepoint.com/sites/siteB"
"https://contoso.sharepoint.com/sites/siteC"

.EXAMPLE
PS C:\> .\SetModernListUsage.ps1
#>

#region Logging and generic functions
function LogWrite
{
    param([string] $log , [string] $ForegroundColor)

    $global:strmWrtLog.writeLine($log)
    if([string]::IsNullOrEmpty($ForegroundColor))
    {
        Write-Host $log
    }
    else
    {    
        Write-Host $log -ForegroundColor $ForegroundColor
    }
}

function LogError
{
    param([string] $log)
    
    $global:strmWrtError.writeLine($log)
}

function UsageLog
{
    try 
    {
        $cc = Get-PnPContext
        $cc.Load($cc.Web)
        $cc.ClientTag = "SPDev:ModernListUsage"
        $cc.ExecuteQuery()
    }
    catch [Exception] { }
}
#endregion

function SiteCollectionUsesModernLists
{
    param([string] $siteCollectionUrl, 
          [Boolean] $useModern,
          $credentials,
          $tenantContext,
          [string] $adminUPN)
    
    
    #region Ensure access to the site collection, if needed promote the calling account to site collection admin
    # Check if we can access the site...if not let's 'promote' ourselves as site admin
    $adminClaim = "i:0#.f|membership|$adminUPN"    
    $adminWasAdded = $false
    $siteContext = $null    
    $siteCollectionUrl = $siteCollectionUrl.TrimEnd("/");

    Try
    {
        LogWrite "User running script: $adminUPN"
        LogWrite "Connecting to site $siteCollectionUrl"
        $siteContext = Connect-PnPOnline -Url $siteCollectionUrl -Credentials $credentials -Verbose -ReturnConnection
    }
    Catch [Exception]
    {
        # If Access Denied then use tenant API to add current tenant admin user as site collection admin to the current site
        if ($_.Exception.Response.StatusCode -eq "Unauthorized")
        {
            LogWrite "Temporarily adding user $adminUPN as site collection admin"
            Set-PnPTenantSite -Url $siteCollectionUrl -Owners @($adminUPN) -Connection $tenantContext
            $adminWasAdded = $true
            LogWrite "Second attempt to connect to site $siteCollectionUrl"
            $siteContext = Connect-PnPOnline -Url $siteCollectionUrl -Credentials $credentials -Verbose -ReturnConnection
        }
        else 
        {
            $ErrorMessage = $_.Exception.Message
            LogWrite "Error for site $siteCollectionUrl : $ErrorMessage" Red
            LogError $ErrorMessage
            return              
        }
    }
    #endregion

    Try
    {
        #region Adding admin
        # Check if current tenant admin is part of the site collection admins, if not add the account        
        $siteAdmins = $null
        if ($adminWasAdded -eq $false)
        {
            try 
            {
                # Eat exceptions here...resulting $siteAdmins variable will be empty which will trigger the needed actions                
                $siteAdmins = Get-PnPSiteCollectionAdmin -Connection $siteContext -ErrorAction Ignore
            }
            catch [Exception] { }
            
            $adminNeedToBeAdded = $true
            foreach($admin in $siteAdmins)
            {
                if ($admin.LoginName -eq $adminClaim)
                {
                    $adminNeedToBeAdded = $false
                    break
                }
            }

            if ($adminNeedToBeAdded)
            {
                LogWrite "Temporarily adding user $adminUPN as site collection admin"
                Set-PnPTenantSite -Url $siteCollectionUrl -Owners @($adminUPN) -Connection $tenantContext
                $adminWasAdded = $true
            }
        }

        UsageLog
        #endregion
        
        #region Enable/disable the modern list experience at site collection level
        if ($useModern)
        {
            LogWrite "Disabling the modern list blocking feature"
            Disable-PnPFeature -Identity "E3540C7D-6BEA-403C-A224-1A12EAFEE4C4" -Scope Site -Force -Connection $siteContext
        }
        else
        {
            LogWrite "Enabling the modern list blocking feature"
            Enable-PnPFeature -Identity "E3540C7D-6BEA-403C-A224-1A12EAFEE4C4" -Scope Site -Force -Connection $siteContext
        }
        #endregion

        #region Cleanup updated permissions
        LogWrite "Configuration is done, let's cleanup the configured permissions"
    
        # Remove the added site collection admin - obviously this needs to be the final step in the script :-)
        if ($adminWasAdded)
        {
            LogWrite "Remove $adminUPN from site collection administrators"            
            Remove-PnPSiteCollectionAdmin -Owners @($adminUPN) -Connection $siteContext
        }
        #endregion

        LogWrite "Configuration done for site collection $siteCollectionUrl" Green
        
        # Disconnect PnP Powershell from site
        Disconnect-PnPOnline
    }
    Catch [Exception]
    {
        $ErrorMessage = $_.Exception.Message
        LogWrite "Error: $ErrorMessage" Red
        LogError $ErrorMessage

        #region Cleanup updated permissions on error
        # Configuration did not complete...remove the added tenant admin to restore site permissions as final step in the cleanup
        if ($adminWasAdded)
        {
            try 
            {
                # Final step, remove the added site collection admin
                Remove-PnPSiteCollectionAdmin -Owners @($adminUPN) -Connection $siteContext
            }
            catch [Exception] { }
        }
        #endregion

        LogWrite "Configuration failed for site collection $siteCollectionUrl" Red
    } 

}

#######################################################
# MAIN section                                        #
#######################################################

# OVERRIDES
# If you want to automate the run and make the script ask less questions, feel free to hardcode these 2 values below. Otherwise they'll be asked from the user or parsed from the values they input

# Tenant admin url
$tenantAdminUrl = "" # e.g. "https://contoso-admin.sharepoint.com"
# If you use credential manager then specify the used credential manager entry, if left blank you'll be asked for a user/pwd
$credentialManagerCredentialToUse = ""

#region Setup Logging
$date = Get-Date
$logfile = ((Get-Item -Path ".\" -Verbose).FullName + "\ModernListUsage_log_" + $date.ToFileTime() + ".txt")
$global:strmWrtLog=[System.IO.StreamWriter]$logfile
$global:Errorfile = ((Get-Item -Path ".\" -Verbose).FullName + "\ModernListUsage_error_" + $date.ToFileTime() + ".txt")
$global:strmWrtError=[System.IO.StreamWriter]$Errorfile
#endregion

#region Load needed PowerShell modules
# Ensure PnP PowerShell is loaded

$minimumVersion = New-Object System.Version("3.4.1812.2")
if (-not (Get-InstalledModule -Name SharePointPnPPowerShellOnline -MinimumVersion $minimumVersion -ErrorAction Ignore)) 
{
    Install-Module SharePointPnPPowerShellOnline -MinimumVersion $minimumVersion -Scope CurrentUser
}
Import-Module SharePointPnPPowerShellOnline -DisableNameChecking -MinimumVersion $minimumVersion
#endregion

#region Gather set modern list usage run input
# Url of the site collection to remediate
$siteCollectionUrlToConfigure = ""
$enableModern = $false

# Get the input information
$siteURLFile = Read-Host -Prompt "Input either single site collection URL (e.g. https://contoso.sharepoint.com/sites/teamsite1) or name of .CSV file (e.g. SitesWithCustomizations.csv) ?"
if (-not $siteURLFile.EndsWith(".csv"))
{
    $siteCollectionUrlToConfigure = $siteURLFile
}
# If we are using a CSV, we'll need to get the tenant admin url from the user or use the hardcoded one
else 
{
    if ($tenantAdminUrl -eq $null -or $tenantAdminUrl.Length -le 0) 
    {
        $tenantAdminUrl = Read-Host -Prompt "Input the tenant admin site URL (like https://contoso-admin.sharepoint.com)"
    }
}

$enableModernString = Read-Host -Prompt "Do you want to enable modern lists and libraries for this site collection? Enter True for yes, False otherwise"
try 
{
    $enableModern = [System.Convert]::ToBoolean($enableModernString) 
} 
catch [FormatException]
{
    $enableModern = $false
}

# We'll parse the tenantAdminUrl from site url (unless it's set already!)
if ($tenantAdminUrl -eq $null -or $tenantAdminUrl.Length -le 0) 
{
    if ($siteURLFile.IndexOf("/teams") -gt 0) 
    {
        $tenantAdminUrl = $siteURLFile.Substring(0, $siteURLFile.IndexOf("/teams")).Replace(".sharepoint.", "-admin.sharepoint.")
    }
    else 
    {
        $tenantAdminUrl = $siteURLFile.Substring(0, $siteURLFile.IndexOf("/sites")).Replace(".sharepoint.", "-admin.sharepoint.")
    }
}

# Get the tenant admin credentials.
$credentials = $null
$adminUPN = $null
if(![String]::IsNullOrEmpty($credentialManagerCredentialToUse) -and (Get-PnPStoredCredential -Name $credentialManagerCredentialToUse) -ne $null)
{
    $adminUPN = (Get-PnPStoredCredential -Name $credentialManagerCredentialToUse).UserName
    $credentials = $credentialManagerCredentialToUse
}
else
{
    # Prompts for credentials, if not found in the Windows Credential Manager.
    $adminUPN = Read-Host -Prompt "Please enter admin UPN (e.g. admin@contoso.onmicrosoft.com)"
    $pass = Read-host -AsSecureString "Please enter admin password"
    $credentials = new-object management.automation.pscredential $adminUPN,$pass
}

if($credentials -eq $null) 
{
    Write-Host "Error: No credentials supplied." -ForegroundColor Red
    exit 1
}
#endregion

#region Connect to SharePoint
# Get a tenant admin connection, will be reused in the remainder of the script
LogWrite "Connect to tenant admin site $tenantAdminUrl"
$tenantContext = Connect-PnPOnline -Url $tenantAdminUrl -Credentials $credentials -Verbose -ReturnConnection
#endregion

#region Configure the site(s)
if (-not $siteURLFile.EndsWith(".csv"))
{
    # Remediate the given site collection
    SiteCollectionUsesModernLists $siteCollectionUrlToConfigure $enableModern $credentials $tenantContext $adminUPN
}
else 
{
    $csvRows = Import-Csv $siteURLFile -Header SiteCollectionUrl
    
    foreach($row in $csvRows)
    {
        if($row.SiteCollectionUrl -ne "")
        {
            $siteUrl = $row.SiteCollectionUrl
            SiteCollectionUsesModernLists $siteUrl $enableModern $credentials $tenantContext $adminUPN
        }
    }
}
#endregion

#region Close log files
if ($global:strmWrtLog -ne $NULL)
{
    $global:strmWrtLog.Close()
    $global:strmWrtLog.Dispose()
}

if ($global:strmWrtError -ne $NULL)
{
    $global:strmWrtError.Close()
    $global:strmWrtError.Dispose()
}
#endregion