次の方法で共有


ファーム ソリューションでの SharePoint コンテンツ タイプとサイト列の置換

この記事では、SharePoint クライアント側オブジェクト モデル (CSOM) を使用して、コンテンツ タイプとサイト列を置換し、新しいコンテンツ タイプにサイト列を追加してから、従来のコンテンツ タイプを新しいコンテンツ タイプに置換するときに使用する変換プロセスについて説明します。

重要

ファーム ソリューションは、SharePoint Online に移行できません。 この記事に記載されている手法やコードを適用することにより、更新されたコンテンツ タイプとサイト列を使用して、同様の機能をファーム ソリューションまたは宣言型サンド ボックス ソリューションに提供する新しいソリューションを構築できます。 構築した新しいソリューションは、SharePoint Online に展開できます。

完全に機能するソリューションを実現するには、この記事のコードに、追加のコードが必要になります。 たとえば、この記事では、Office 365 に対する認証を実行する方法や、必要な例外処理を実装する方法などについては取り上げていません。 追加のコード サンプルについては、「Office 365 Developer Patterns and Practices プロジェクト」を参照してください。

注:

この記事で提供されるコードは、明示または黙示のいかなる種類の保証なしに現状のまま提供されるものであり、特定目的への適合性、商品性、権利侵害の不存在についての暗黙的な保証は一切ありません。

CSOM を使用して、コンテンツ タイプとサイト列を置換するには、次のようにします。

  1. 新しいコンテンツ タイプを作成します。

  2. 新しいサイト列 (フィールドとも呼ばれる) を作成します。

  3. 新しいサイト列を新しいコンテンツ タイプに追加します。

  4. 古いコンテンツ タイプの参照を新しいコンテンツ タイプに置換します。

次のコードで、Main は CSOM を使用してコンテンツ タイプとサイト列を置換するために実行する操作の順序を示しています。

static void Main(string[] args)
{
    using (var clientContext = new ClientContext("http://contoso.sharepoint.com"))
    {

        Web web = clientContext.Web;
        
        CreateContentType(clientContext, web);
        CreateSiteColumn(clientContext, web);
        AddSiteColumnToContentType(clientContext, web);

        // Replace the old content type with the new content type.
        ReplaceContentType(clientContext, web);
    }

}

次のコードで、GetContentTypeByName は、現在のサイトから以下のようにしてコンテンツ タイプを取得します。

  1. Web.ContentTypes プロパティを使用して ContentTypeCollection (現在のサイトにおけるコンテンツ タイプのコレクション) を取得します。

  2. サイトからコンテンツ タイプを検索して返します。その場合、サイトのコンテンツ タイプ名を既存のコンテンツ タイプ名と突き合わせます。コンテンツ タイプ名は name パラメーターで送信されます。

    private static ContentType GetContentTypeByName(ClientContext cc, Web web, string name)
    {
        ContentTypeCollection contentTypes = web.ContentTypes;
        cc.Load(contentTypes);
        cc.ExecuteQuery();
        return contentTypes.FirstOrDefault(o => o.Name == name);
    }

新しいコンテンツ タイプを作成する

次のコードで、CreateContentType は、次のようにして新しいコンテンツ タイプを作成します。

  1. コンテンツ タイプの名前を格納するための contentTypeName という名前の定数を作成します。 新しいコンテンツ タイプの名前は、以前のコンテンツ タイプの名前に設定されます。

  2. GetContentTypeByName を呼び出して、サイト上で一致するコンテンツ タイプを検索します。

  3. コンテンツ タイプが既に存在する場合は、必要な操作はなく、return が呼び出されたときに制御が Main に戻されます。

    コンテンツ タイプが存在しない場合は、newCt という名前の ContentTypeCreationInformation オブジェクトを使用して、コンテンツ タイプのプロパティが設定されます。

    基本ドキュメント コンテンツ タイプ ID 0x0101 を使用して、新しいコンテンツ タイプ ID が newCt.Id に割り当てられます。 詳細については、「コンテンツ タイプの基本的な階層」を参照してください。

  4. ContentTypeCollection.Add を使用して、新しいコンテンツ タイプを追加します。

private static void CreateContentType(ClientContext cc, Web web)
{
    // The new content type will be created using this name.
    const string contentTypeName = "ContosoDocumentByCSOM";

    // Determine whether the content type already exists.
    var contentType = GetContentTypeByName(cc, web, contentTypeName);

    // The content type exists already. No further action required.
    if (contentType != null) return;

    // Create the content type using the ContentTypeInformation object.
    ContentTypeCreationInformation newCt = new ContentTypeCreationInformation();
    newCt.Name = "ContosoDocumentByCSOM";

    // Create the new content type based on the out-of-the-box document (0x0101) and assign the ID to the new content type.
    newCt.Id = "0x0101009189AB5D3D2647B580F011DA2F356FB2";

    // Assign the content type to a specific content type group.
    newCt.Group = "Contoso Content Types";

    ContentType myContentType = web.ContentTypes.Add(newCt);
    cc.ExecuteQuery();
}

新しいサイト列を作成する

次のコードで、CreateSiteColumn は、以下のようにして、新しいサイト列を作成します。

  1. フィールドの名前を格納するための fieldName という名前の定数を作成します。 新しいフィールドの名前は、以前のフィールドの名前に設定されます。

  2. Web.Fields プロパティを使用して、サイト上に定義されているサイト列を取得します。

  3. サイト上のフィールド名を fieldName と照合することにより、サイト上で一致するフィールドを検索します。 フィールドが既に存在する場合は、必要な操作はなく、return が呼び出されたときに制御が Main に戻されます。 フィールドが存在しない場合は、フィールド スキーマを指定する CAML 文字列が FieldAsXML に割り当てられてから、FieldCollection.AddFieldAsXml を使用してフィールドが作成されます。

private static void CreateSiteColumn(ClientContext cc, Web web)
{
    // The new field will be created using this name.
    const string fieldName = "ContosoStringCSOM";

    // Load the list of fields on the site.
    FieldCollection fields = web.Fields;
    cc.Load(fields);
    cc.ExecuteQuery();

    // Check fields on the site for a match.
    var fieldExists = fields.Any(f => f.InternalName == fieldName);

     // The field exists already. No further action required.    
    if (fieldExists) return;

    // Field does not exist, so create the new field.
    string FieldAsXML = @"<Field ID='{CB8E24F6-E1EE-4482-877B-19A51B4BE319}' 
                                Name='" + fieldName + @"' 
                                DisplayName='Contoso String by CSOM' 
                                Type='Text' 
                                Hidden='False' 
                                Group='Contoso Site Columns' 
                                Description='Contoso Text Field' />";
    Field fld = fields.AddFieldAsXml(FieldAsXML, true, AddFieldOptions.DefaultValue);
    cc.ExecuteQuery();
}

新しいサイト列を新しいコンテンツ タイプに追加する

次のコードで、AddSiteColumnToContentType は、コンテンツ タイプとフィールド間の接続を以下のようにして作成します。

  1. コンテンツ タイプを読み込み、そのコンテンツ タイプを ContentType.FieldLinks プロパティを使用してフィールドが参照します。

  2. フィールドを読み込みます。

  3. contentType.FieldLinks.Any(f => f.Name == fieldName) を使用して、コンテンツ タイプがフィールドを参照しているかどうかを判断します。

  4. コンテンツ タイプが対象フィールドを既に参照している場合、追加アクションは不要で、 return が呼び出されると制御が Main に戻ります。 コンテンツ タイプが対象フィールドを参照していない場合には、フィールド参照プロパティは FieldLinkCreationInformation オブジェクトで設定されます。

  5. FieldLinkCreationInformation オブジェクトを ContentType.FieldLinks プロパティに追加します。

private static void AddSiteColumnToContentType(ClientContext cc, Web web)
{
    // The name of the content type. 
    const string contentTypeName = "ContosoDocumentByCSOM";
    // The field name.
    const string fieldName = "ContosoStringCSOM";

    // Load the content type.
    var contentType = GetContentTypeByName(cc, web, contentTypeName);
    if (contentType == null) return; // content type was not found

    // Load field references in the content type.
    cc.Load(contentType.FieldLinks);
    cc.ExecuteQuery();

    // Load the new field.
    Field fld = web.Fields.GetByInternalNameOrTitle(fieldName);
    cc.Load(fld);
    cc.ExecuteQuery();

    // Determine whether the content type refers to the field.
    var hasFieldConnected = contentType.FieldLinks.Any(f => f.Name == fieldName);

    // A reference exists already, no further action is required.
    if (hasFieldConnected) return;

    // The reference does not exist, so we have to create the reference.
    FieldLinkCreationInformation link = new FieldLinkCreationInformation();
    link.Field = fld;
    contentType.FieldLinks.Add(link);
    contentType.Update(true);
    cc.ExecuteQuery();
}

古いコンテンツ タイプの参照を新しいコンテンツ タイプに置換する

次のコードで、ReplaceContentType は、すべてのライブラリにある項目すべてで古いコンテンツ タイプを参照するコンテンツを検査し、こうした参照を新しいコンテンツ タイプ (ContosoDocumentByCSOM) に以下のようにして置換します。

  1. 古いコンテンツ タイプ ID を定数に割り当てます。

  2. GetContentTypeByName を使用して、新しいコンテンツ タイプを取得します。

  3. Web.Lists を使用して、サイト上のすべてのリストを取得します。

  4. cc.Load(lists, l => l.Include(list => list) を使用して、サイト上のすべてのリストと各リストのすべてのコンテンツ タイプを読み込みます。ContentTypes)

  5. 返されたリストごとに、リストを使用して、リストのコンテンツ タイプを検索して、コンテンツ タイプと古いコンテンツ タイプ ID を一致 させます。ContentTypes.Any(c => c.StringId.StartsWith(oldContentTypeId)))。 一致項目が見つかると、古いコンテンツ タイプが含まれるリストが listsWithContentType に追加されます。

  6. listsWithContentType の各リストについて、以下を行います。

  7. 新しいコンテンツ タイプがリストに追加されているかどうかを判別します。 その新しいコンテンツ タイプがリストに追加されていない場合、 ContentTypeCollection.AddExistingContentType を使用して、対象の新しいコンテンツ タイプをリストに追加します。

  8. リストのすべての項目を取得します。

  9. それぞれのリスト項目に関して、リスト項目のコンテンツ タイプ ID を取得します。 リスト項目のコンテンツ タイプ ID が古いコンテンツ タイプ ID と等しいかどうかを判別します。 対象コンテンツ タイプ ID が等しくない場合、次のリスト項目にスキップます。 対象コンテンツ タイプ ID が等しい場合、 ContentType.StringId を使用して、その新しいコンテンツ タイプ ID をリスト項目に割り当てます。

注:

古いコンテンツ タイプはリストにあるものの、既に使用されていません。 これで、古いコンテンツ タイプをリストから削除してから、取り消すことできます。 この記事では、ドキュメント コンテンツ タイプを置換する方法のみについて説明します。 ページ レイアウトでコンテンツ タイプを置換する場合は、サイト コレクションの各ページ レイアウトで AssociatedContentType プロパティを必ず更新してください。

private static void ReplaceContentType(ClientContext cc, Web web)
{
    // The old content type. 
    const string oldContentTypeId = "0x010100C32DDAB6381C44868DCD5ADC4A5307D6";
    // The new content type name.
    const string newContentTypeName = "ContosoDocumentByCSOM";

    // Get the new content type and lists on the site.
    ContentType newContentType = GetContentTypeByName(cc, web, newContentTypeName);
    ListCollection lists = web.Lists;
    
    // Load the new content type and the content types on all lists on the site. 
    cc.Load(newContentType);
    cc.Load(lists,
            l => l.Include(list => list.ContentTypes));
    cc.ExecuteQuery();
    var listsWithContentType = new List<List>();
    foreach (List list in lists)
    {
        bool hasOldContentType = list.ContentTypes.Any(c => c.StringId.StartsWith(oldContentTypeId));
        if (hasOldContentType)
        {
            listsWithContentType.Add(list);
        }
    }
    foreach (List list in listsWithContentType)
    {
        // Determine whether the new content type is already attached to the list.
        var listHasContentTypeAttached = list.ContentTypes.Any(c => c.Name == newContentTypeName);
        if (!listHasContentTypeAttached)
        {
            // Attach content type to list.
            list.ContentTypes.AddExistingContentType(newContentType);
            cc.ExecuteQuery();
        }
        // Get all list items.
        CamlQuery query = CamlQuery.CreateAllItemsQuery();
        ListItemCollection items = list.GetItems(query);
        cc.Load(items);
        cc.ExecuteQuery();

        // For each list item, determine whether the old content type is used, and then update to the new content type. 
        foreach (ListItem listItem in items)
        {
            // Get the current content type for this list item.
            var currentContentTypeId = listItem["ContentTypeId"] + "";
            var isOldContentTypeAssigned = currentContentTypeId.StartsWith(oldContentTypeId);

            // This item does not use the old content type - skip to next list item.
            if (!isOldContentTypeAssigned) continue;

            // Update the list item content type to the new content type.
            listItem["ContentTypeId"] = newContentType.StringId; // new content type Id;
            listItem.Update();
        }
        // Save all changes.
        cc.ExecuteQuery();
    }
}

関連項目