最大化新式列表和库的使用

本文讨论如何使用 SharePoint 新式用户界面时获得最大数量的列表和库。。

尚无法转换所有列表和库至新式体验,因为:

  • SharePoint 团队尚未将一些类型的列表和库生成为在新式用户界面中显示;例如,任务列表或事件列表。 请等待 SharePoint 团队实现新式版本,或切换为一个等效选项。 选项包括使用 Microsoft Planner 而不是经典的任务列表,或是使用 Office 365 组日历,而不是经典的 SharePoint 事件列表日历。

  • 部分类型的列表和库虽可以使用新式用户界面显示,但却因配置或自定义不兼容而受阻;你可以在此处采取措施。

重要

新式化工具和所有其他 PnP 组件均为开源工具,由一个活跃社区提供支持服务。 来自 Microsoft 官方支持渠道的开放源代码工具支持不提供任何 SLA。

新式用户介面可用列表模板

以下是 SharePoint 当前在新式用户界面中可以呈现的最常用的列表模板类型(自 2018 年 10 月起):

  • 列表 (100)
  • 文档库 (101)
  • 链接列表 (103)
  • 通知列表 (104)
  • 联系人列表 (105)
  • 图片库 (109)
  • 表单库 (115)
  • 网站页面库 (119)
  • 自定义网格 (120)
  • 提升的链接列表 (170)
  • 发布页面库 (850)
  • 资产库 (851)
  • 问题跟踪列表 (1100)

在本节中,你将了解如何识别未显示为新式列表的列表、原因以及如何进行修正(如果可能)。 然而,在进入讨论之前,重要的是要知道,对于大多数租户,我们看到大多数列表都能作为新式列表进行加载,而没有发出任何警告。 无法作为新式列表进行加载的列表将保留为经典体验(如日历或任务列表),你也可以通过修正它们来解除阻止,这将在本系列文章中进行讨论。 无法作为新式列表进行加载的列表将 100% 可用,并且完全受支持,它们应该不会阻止你为租户启用新式列表和库体验。

重要

SharePoint 将自动默认为新式列表和库,但也会在需要时自动回退。 这将确保所有可以使用新式体验的列表都将显示为新式列表,而这些列表在使用时不能简单地切换为经典体验。

在新式用户介面中检测到列表和库不可用

若要检测哪些列表和库在新式用户界面中不可用,建议方法是运行 SharePoint 新式扫描程序。 此工具会深入分析租户中的所有列表和库,并通过生成报告来详细说明哪些列表和库未使用新式用户界面显示。更重要的是,报告还说明了具体原因。 根据扫描程序输出,可以通过修正列表和库来取消阻止它们,在下一章节中会提到。

取消阻止列表和库

导致列表受阻的原因可能有一个或多个,扫描程序输出详细说明了所有可能原因。 下面列出了常见原因及其修正方法。

自定义不兼容

自定义不兼容是新式用户界面无法呈现列表的最常见原因,通常是由于下列操作之一引起:

  • 使用 JSLink
  • 使用嵌入 JavaScript 的用户自定义操作

若要修复这些阻止原因,可以删除自定义(如果不再与业务相关的话),也可以创建备用解决方案。 有关构建与新式列表和库体验兼容的自定义项的详细信息,请参阅文章新式化自定义项。 由于 Web 部件级别的 JSLink 确实会阻止页面使用新式列表和库经验,因此你需要删除此配置。 你可以通过使页面处于编辑模式来手动执行此操作,具体方法是将 ?ToolPaneView=2&pagemode=edit 附加到页面 URL,然后更新 Web 部件属性。 若要以编程方式执行此操作,你可以通过 LimitedWebPartManager 类获取 Web 部件,然后更新相应的属性,如下面的代码段所示。 你可以将此代码段与本页后面显示的更完整的代码结合使用,以获得完整的解决方案。

webPart.Properties["JSLink"] = "";
webPart.SaveWebPartChanges();
cc.ExecuteQuery();

存在某些字段类型

BCS 外部数据、地理位置、编辑模式下的结果选择、图像、HTML 和摘要链接等一些字段类型尚未生成为使用新式用户界面显示。 若要修正这些字段类型,可以应用下列方法:

  • 从视图中删除字段。 字段仍处于编辑模式,因此获得的是经典编辑体验,但至少视图是新式的。
  • 将字段数据迁移到与新式用户界面兼容的新字段。
  • 完全删除未使用的字段。

在站点或 web 级别阻止的新式和库用户界面

可以为完整的网站集(站点级别)或一个或多个网站(web 级别)阻止新式列表和库用户界面。 通过禁用各自的站点或 Web 范围内的功能,可以修复此操作,如下面 PnP PowerShell 代码片段所示:

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

Connect-PnPOnline -Url "<your site url>"

# Disable the modern list site level blocking feature
Disable-PnPFeature -Identity "E3540C7D-6BEA-403C-A224-1A12EAFEE4C4" -Scope Site
# Disable the modern list web level blocking feature
Disable-PnPFeature -Identity "52E14B6F-B1BB-4969-B89B-C4FAA56745EF" -Scope Web

注意

PnP PowerShell 是一种开放源代码解决方案,其中包含为其提供支持的活动社区。 没有用于 Microsoft 开放源代码工具支持的 SLA。

在列表级别阻止的新式和库用户界面

可以定义列表以始终在列表级别运行经典体验(ListExperienceOptions 设置为 ClassicExperience)。 若要撤销此操作,可以使用以下代码段:

// Load the list you want to update
var list = context.Web.Lists.GetByTitle(title);
context.Load(list);
context.ExecuteQuery();

// Possible options are Auto (= defaults to modern), NewExperience (= "modern") and ClassicExperience
list.ListExperienceOptions = ListExperience.Auto;

// Persist the changes
list.Update();
context.ExecuteQuery();

显示 BaseTemplate = 0 的列表

每个列表都有一个基本模板,但有时你可能会在扫描程序输出中看到基本模板值为 0 的列表。 这是因为对于这些列表,没有标记为默认的视图,或者有时根本没有视图。 对于这些列表,解决方法是导航到列表设置页面 (_layouts/15/listedit.aspx?List=%7B<list id>%7D) 并为列表提供视图。

自定义包含除列表 XSLTListViewWebPart 以外内容的列表视图页面

由于用户可以编辑经典列表视图和编辑页面,因此能够在列表视图页面上添加其他 Web 部件(举个例子)。 如果你完成此操作,则新式用户界面中不再显示列表。 若要修正此问题,可以采用的唯一方法是,从列表页面中删除已添加的 Web 部件。 你可以通过转到列表并将 ?ToolPaneView=2&pagemode=edit 附加到列表 URL 来手动检查这些情况。 这将使页面处于编辑模式,并且应该会显示其他 Web 部件并允许你删除它们。 如果你想以编程方式执行相同的操作,则下面的代码段是一个很好的起始基础。 此代码段基于 PnP 网站核心库,你可以通过 SharePointPnPCoreOnline nuget 包将它安装到 Visual Studio 项目。

using Microsoft.SharePoint.Client;
using Microsoft.SharePoint.Client.WebParts;
using OfficeDevPnP.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security;

namespace MultipleWebPartFixer
{
    class Program
    {
        static void Main(string[] args)
        {
            string siteUrl = "https://contoso.sharepoint.com/sites/demo";
            string userName = "joe@contoso.onmicrosoft.com";
            AuthenticationManager am = new AuthenticationManager();
            using (var cc = am.GetSharePointOnlineAuthenticatedContextTenant(siteUrl, userName, GetSecureString("Password")))
            {
                // Grab the list
                var list = cc.Web.Lists.GetByTitle("listtofix");
                list.EnsureProperties(l => l.RootFolder, l => l.Id);

                bool isNoScriptSite = cc.Web.IsNoScriptSite();

                if (isNoScriptSite)
                {
                    throw new Exception("You don't have the needed permissions to apply this fix!");
                }

                // get the current (allitems) form
                var files = list.RootFolder.FindFiles("allitems.aspx");
                var allItemsForm = files.FirstOrDefault();
                if (allItemsForm != null)
                {
                    // Load web part manager and web parts
                    var limitedWPManager = allItemsForm.GetLimitedWebPartManager(PersonalizationScope.Shared);
                    cc.Load(limitedWPManager);

                    // Load the web parts on the page
                    IEnumerable<WebPartDefinition> webParts = cc.LoadQuery(limitedWPManager.WebParts.IncludeWithDefaultProperties(wp => wp.Id, wp => wp.ZoneId, wp => wp.WebPart.ExportMode, wp => wp.WebPart.Title, wp => wp.WebPart.ZoneIndex, wp => wp.WebPart.IsClosed, wp => wp.WebPart.Hidden, wp => wp.WebPart.Properties));
                    cc.ExecuteQueryRetry();

                    List<WebPartDefinition> webPartsToDelete = new List<WebPartDefinition>();
                    if (webParts.Count() > 1)
                    {
                        // List all except the XsltListView web part(s)
                        foreach (var webPart in webParts)
                        {
                            if (GetTypeFromProperties(webPart.WebPart.Properties) != "XsltListView")
                            {
                                webPartsToDelete.Add(webPart);
                            }
                        }

                        if (webPartsToDelete.Count == webParts.Count() - 1)
                        {
                            foreach(var webPart in webPartsToDelete)
                            {
                                webPart.DeleteWebPart();
                            }
                            cc.ExecuteQueryRetry();
                            Console.WriteLine("List fixed!");
                        }
                        else
                        {
                            // Special case...investigation needed. Go to list and append ?ToolPaneView=2&pagemode=edit to the list url to check the page
                            Console.WriteLine("Go to list and append ?ToolPaneView=2&pagemode=edit to the list url to check this page");
                        }
                    }
                }
            }

            Console.WriteLine("Press enter to continue...");
            Console.ReadLine();
        }

        public static string GetTypeFromProperties(PropertyValues properties)
        {
            // Check for XSLTListView web part
            string[] xsltWebPart = new string[] { "ListUrl", "ListId", "Xsl", "JSLink", "ShowTimelineIfAvailable" };
            if (CheckWebPartProperties(xsltWebPart, properties))
            {
                return "XsltListView";
            }

            return "";
        }
        private static bool CheckWebPartProperties(string[] propertiesToCheck, PropertyValues properties)
        {
            bool isWebPart = true;
            foreach (var wpProp in propertiesToCheck)
            {
                if (!properties.FieldValues.ContainsKey(wpProp))
                {
                    isWebPart = false;
                    break;
                }
            }

            return isWebPart;
        }

        private static SecureString GetSecureString(string label)
        {
            SecureString sStrPwd = new SecureString();
            try
            {
                Console.Write(String.Format("{0}: ", label));

                for (ConsoleKeyInfo keyInfo = Console.ReadKey(true); keyInfo.Key != ConsoleKey.Enter; keyInfo = Console.ReadKey(true))
                {
                    if (keyInfo.Key == ConsoleKey.Backspace)
                    {
                        if (sStrPwd.Length > 0)
                        {
                            sStrPwd.RemoveAt(sStrPwd.Length - 1);
                            Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
                            Console.Write(" ");
                            Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
                        }
                    }
                    else if (keyInfo.Key != ConsoleKey.Enter)
                    {
                        Console.Write("*");
                        sStrPwd.AppendChar(keyInfo.KeyChar);
                    }

                }
                Console.WriteLine("");
            }
            catch (Exception e)
            {
                sStrPwd = null;
                Console.WriteLine(e.Message);
            }

            return sStrPwd;
        }
    }
}

另请参阅