65.9K
CodeProject 正在变化。 阅读更多。
Home

SharePoint 2010 分类法导入

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (4投票s)

2011年5月9日

CPOL

6分钟阅读

viewsIcon

48252

downloadIcon

518

将分类法层次结构导入 SharePoint 2010

引言

SharePoint 2010 添加了一个非常有用的功能,即托管元数据 (Managed Metadata),它提供了一个易于使用的界面来添加、保护和维护创建的术语(Taxonomies)。然而,导入术语并没有一个非常强大的工具,而且通常使用起来很繁琐。

在本文中,我将探讨 SharePoint 2010 中如何创建托管元数据,并提出一种轻松导入术语层次结构的方法。

托管元数据

托管元数据是您定义的、可集中管理的术语的层次结构集合,然后可以将其用作 Microsoft SharePoint Server 2010 中项目的属性。- MSDN (http://msdn.microsoft.com/en-us/library/ee559337.aspx)。创建的术语可用于标记 SharePoint 中的内容,从而更轻松地搜索项目或对其进行分类和筛选。在 SharePoint 2010 中,层次结构从 **托管元数据术语存储 (Managed Metadata Term Store)** 开始,并按 **组 (Groups)** 组织,组包含 **术语集 (TermSets)**,术语集又包含 **术语 (Terms)**,术语也可以包含其他 **术语**。下图是一个演示此组织的示例。

Image1.PNG

创建术语和术语集

要在 SharePoint 2010 中通过 UI 创建托管元数据,您需要使用 `术语存储管理工具 (Term Store Management Tool)`,该工具可以从 **中央管理 (Central Administration)** 站点中的 **托管元数据服务 (Managed Metadata Service)** 访问

Image2.PNG

或从站点 (Site) 的站点设置 (Site Settings) 页面上的术语存储管理链接访问。

Image3.PNG

进入管理工具后,要添加一个新的托管元数据组,将鼠标悬停在托管元数据服务项目 (Managed Metadata Service item) 的末尾,会显示一个箭头,点击展开菜单,然后点击新建组 (New Group) 菜单项。

Image4.PNG

添加组后,屏幕右侧将显示可用于为该组指定管理器和贡献者的几个区域。由于这些设置与其他 SharePoint 配置类型类似,我在此不做讨论,而是继续专注于术语集和术语的创建。

创建组后,可以使用其上的菜单创建一个术语集。

Image5.PNG

我们暂时跳过导入术语集 (Import Term Set) 项,只创建一个新的术语集 (New Term Set)。同样,创建术语集后,屏幕右侧将显示可应用于它的各种设置。

再次使用术语集上的菜单,术语集级别还有一些其他选项可用。

Image6.PNG

使用这些菜单项,您可以将术语集复制到另一个组,这在您有两个不同的组具有相同的结构时非常有用,可以节省重新输入所有信息的时间。术语集也可以移动到另一个组,而不是重新输入并从现有组中删除。

创建术语集后,您可以向其中添加术语,以及向术语中添加术语。您可以设置最多七层的深度,但是,实际使用会根据您的需求限制深度。

导入术语和术语集

以上所有步骤对于添加少量项目都可以,但当尝试输入新的层次结构,尤其是大型或复杂的层次结构时,会很快变得繁琐。幸运的是,Microsoft 创建了一种批量导入术语集的方法,我们在早期在组级别看到了使用“导入术语集”菜单项。点击此菜单项会显示一个模态对话框,要求提供一个 CSV 格式的文件进行导入。文件结构的示例可以在术语存储管理工具的托管元数据服务级别找到。

Image7.PNG

文件内容,与大多数 CSV 格式一样,不易阅读。不过,您应该能从标题行中看到可以提供的信息。虽然文件本身没有验证,但显而易见的是,必须提供术语集和术语的名称。导入期间,如果文件不正确,过程将失败。

"Term Set Name","Term Set Description","LCID","Available for Tagging",
"Term Description","Level 1 Term","Level 2 Term","Level 3 Term",
"Level 4 Term","Level 5 Term","Level 6 Term","Level 7 Term"
"Political Geography","A sample term set, describing a simple political geography.",,
True,"One of the seven main land masses (Europe, Asia, Africa, North America, 
South America, Australia, and Antarctica)","Continent",,,,,,
,,,True,"Entity defined by people, not visible to the naked eye",
"Continent","Political Entity",,,,,
,,,True,"Politically defined state with a geographic area governed by a 
central government","Continent","Political Entity","Country",,,,
,,,True,"Administrative division of a country","Continent",
"Political Entity","Country","Province or State",,,
,,,True,"Large sub-region usually containing many cities and towns",
"Continent","Political Entity","Country","Province or State","County or Region",,
,,,True,"Small village","Continent","Political Entity","Country",
"Province or State","County or Region","Hamlet",
,,,True,"Collection of homes and business, often incorporated",
"Continent","Political Entity","Country","Province or State",
"County or Region","Village",
,,,True,"A small city","Continent","Political Entity","Country",
"Province or State","County or Region","Town",
,,,True,"An incorporated town with a large population, usually governed by a mayor 
or council","Continent","Political Entity","Country","Province or State",
"County or Region","City",
,,,True,"A division of a city, often represented in the city government",
"Continent","Political Entity","Country","Province or State","County or Region",
"City","District"
,,,True,"A sub-section of a city","Continent","Political Entity","Country",
"Province or State","County or Region","City","Borough"
,,,True,"Unofficial district or area of a city or town","Continent",
"Political Entity","Country","Province or State","County or Region","City","Neighborhood"

这种自动化方法很有帮助,但存在一些缺点。首先,文件格式难以阅读,即使使用 Excel 也很难从平面文件中看到层次结构。其次,一次只能导入一个术语集。如果您需要向一个组添加多个术语集,则必须为每个术语集使用多个文件运行导入过程。

导入术语层次结构

为了克服开箱即用的托管元数据导入方法的缺点,我使用 XML 文件以直观的方式指定层次结构,并创建了一个应用程序页面,该页面可以读取文件并创建其中找到的术语集和术语。

 <Taxonomy lcid="1033">
  <TermStore name="Managed Metadata Service">
    <Group name="CodeProject">
      <TermSet name="General Programming">
        <Term name=".NET Framework" guid="{fc456058-65a1-45b8-9b50-fbb561cba9c0}" />
      </TermSet>
    </Group>
  </TermStore>
</Taxonomy> 

与之前的 CSV 文件相比,这个文件更容易阅读,也更容易看到将要创建的层次结构。元素的 `name` 属性当然是应用于组、术语集和术语的名称。`guid` 属性稍后将进行解释。

应用程序页面的代码非常简单,不需要复杂的东西,只需上传一个文件并将其发送到辅助方法进行处理。

protected void OnImport(object sender, EventArgs e)
{
    if(FileUpload.HasFile)
    {
        try
        {
            TextReader reader = new StreamReader(FileUpload.PostedFile.InputStream);
            MANSoftDev.SharePoint.Utilities.Taxonomy.TaxonomyHelper.ImportTerms
					(SPContext.Current.Site, reader);

            msg.InnerText = "Import complete";
            msg.Visible = true;
        }
        catch(Exception ex)
        {
            msg.InnerText = ex.Message;
            msg.Visible = true;
        }
    }
    else
    {
        msg.InnerText = "Please select a file.";
        msg.Visible = true;
    }
}

辅助方法也不复杂。首先,将上传的文件流 **加载 (Load)** 到一个 **XElement** 中,以便进行处理。在加载 **XElement** 后,只需遍历层次结构并创建相应的元素即可。

public static void ImportTerms(SPSite site, TextReader metadata)
{
    Site = site;
    XElement taxonomy = XElement.Load(metadata);
    if(taxonomy.Attribute("lcid") != null)
    {
        LCID = Convert.ToInt32(taxonomy.Attribute("lcid").Value);
    }
    else
    {
        LCID = 1033;
    }

    // Get the TaxonomySession from the specified site
    TaxonomySession session = new TaxonomySession(site);

    foreach(XElement termStoreElement in taxonomy.Elements())
    {
        // Get the TermStore from the import file
        TermStore termStore = session.TermStores[termStoreElement.Attribute("name").Value];
        if(termStore != null)
        {
            foreach(XElement groupElement in termStoreElement.Elements())
            {
                // Check if the Group already exits and create it if not
                Group group = termStore.Groups.FirstOrDefault
				(e => e.Name == groupElement.Attribute("name").Value);
                if(group == null)
                {
                    group = termStore.CreateGroup(groupElement.Attribute("name").Value);
                }

                foreach(XElement termsetElement in groupElement.Elements())
                {
                    CreateTermsSets(termsetElement, group);
                }
            }
        }

        // Commit everything that has been added
        termStore.CommitAll();
    }
}

`CreateTerms` 方法是稍微复杂一些的部分,也是 `guid` 属性使用的地方。

private static void CreateTerms(XElement element, TermSet set)
{
    Term term = set.Terms.FirstOrDefault(e => e.Name == element.Attribute("name").Value);
    if(term == null)
    {
        term = set.CreateTerm(element.Attribute("name").Value, LCID,
            new Guid(element.Attribute("guid").Value));

        int[] ids = TaxonomyField.GetWssIdsOfTerm
			(Site, set.Group.TermStore.Id, set.Id, term.Id, false, 999);
        if(ids.Length == 0)
        {
            int id = CreateWssId(Site, term);
        }
    }
}

与 SharePoint 中的许多事物一样,GUID 用于唯一标识跨站点、列表、应用程序和其他位置的工件。托管元数据也不例外。如您所见,`TermSet.CreateTerm` 方法的一个重载接受一个 GUID,该 GUID 将用于标识 **术语 (Term)**。如果未指定,则会为其创建一个 GUID。当将功能部署到多个服务器时,这可能会导致问题,因为每台服务器都会为 **术语** 创建一个不同的 GUID。使用重载方法,我们可以确保 GUID 相同,并且可以在代码和其他配置文件(如 **内容类型 (ContentType)** 或 **字段 (Field)** 声明)中一致地引用它。

此代码的另一个有趣之处是 `TaxonomyField.GetWssIdsOfTerm` 方法。SharePoint 使用一个隐藏列表来维护已使用的 **术语 (Taxonomy Terms)**,并为列表中的每个术语添加一个 ID。**术语** 仅在被使用时(例如,通过在 **术语选择器 (TaxonomyPicker)** 控件中选中并添加到列表)才会被添加。但是,仅将 **术语** 添加到 **术语集 (TermSet)** 并不足以触发此操作。然而,我们可以强制执行此操作。尽管没有公开可访问的方法可以做到这一点,但有一个方法可以使用反射来实现,如下所示:

private static int CreateWssId(SPSite site, Term term)
{
    site.RootWeb.AllowUnsafeUpdates = true;

    int result = -1;

    MethodInfo mi = typeof(TaxonomyField).GetMethod("AddTaxonomyGuidToWss",
            BindingFlags.NonPublic | BindingFlags.Static, null,
            new Type[3] { typeof(SPSite), typeof(Term), typeof(bool) },
            null);
    if(mi != null)
    {
        result = (int)mi.Invoke(null, new object[3] { site, term, false });
    }

    site.RootWeb.AllowUnsafeUpdates = false;

    return result;
}

访问功能

以上所有代码都可以正常工作,但您需要一种方法来访问和使用它。为此,我在中央管理站点的应用程序管理页面上创建了一个新组。为了方便起见,我还创建了一个指向术语存储管理器的链接。

<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <CustomActionGroup
     Id="MANSoftDev.Group"
     Location="Microsoft.SharePoint.Administration.Applications"
     Title="Managed Metadata" 
     ImageUrl="_layouts/Images/PORT_MAP.GIF"/>
  <CustomAction
    Id="MANSoftDev.ManagedMetaData"
    Title="Term store management"
    GroupId="MANSoftDev.Group"
    Location="Microsoft.SharePoint.Administration.Applications">
      <UrlAction Url="_layouts/termstoremanager.aspx"/>
  </CustomAction>
  <CustomAction
    Id="MANSoftDev.ManagedMetaData"
    Title="Import Managed Metadata"
    GroupId="MANSoftDev.Group"
    Location="Microsoft.SharePoint.Administration.Applications">
      <UrlAction Url="_layouts/MANSoftDev/TaxonomyImport/ImportMetadata.aspx"/>
  </CustomAction>
</Elements>

结论

正如我所演示的,导入托管元数据并不是一个非常复杂的过程,但我希望已经解释了可用的方法,并提供了一个必要的实用程序,使其更加方便。

历史

  • 首次发布:2011 年 5 月 9 日
© . All rights reserved.