Замена типов контента и столбцов сайта SharePoint в решениях ферм
В этой статье описывается процесс преобразования, используемый при замене типов контента и столбцов сайта, добавлении столбцов сайта в новые типы контента, а затем при замене предыдущих типов контента новыми типами контента с помощью клиентской объектной модели (CSOM) SharePoint.
Важно!
Решения фермы нельзя перенести в SharePoint Online. Применяя методы и код, описанные в этой статье, можно создать новое решение, которое использует обновленные типы контента и столбцы сайта и предоставляет аналогичные функции решениям фермы или декларативным решениям песочницы. Затем новое решение можно развернуть в SharePoint Online.
Код, приведенный в этой статье, требует дополнительного кода, чтобы обеспечить полностью работающее решение. Например, в этой статье не рассматривается проверка подлинности для Office 365, реализация необходимой обработки исключений и т. д. Дополнительные примеры кода см. в проекте шаблоны и методики разработчика Office 365.
Примечание.
Код, приведенный в этой статье, предоставляется "как есть" без какой-либо явной или подразумеваемой гарантии, включая подразумеваемые гарантии пригодности для какой-либо цели, для продажи или гарантии отсутствия нарушения прав иных правообладателей.
Чтобы заменить типы контента и столбцы сайта с помощью CSOM, выполните следующие действия:
Создайте новый тип контента.
Создайте новый столбец сайта (также называемый полем).
Добавьте новый столбец сайта в новый тип контента.
Замените ссылки на старые типы контента новым типом контента.
В следующем коде в разделе 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 получает тип контента с текущего сайта:
Использование свойства Web.ContentTypes для получения ContentTypeCollection, который представляет собой коллекцию типов контента на текущем сайте.
Поиск и возврат типа контента с сайта путем сопоставления имени типа контента сайта с именем существующего типа контента, который отправляется с помощью параметра 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 создает новый тип контента:
Создание константы с именем contentTypeName для хранения имени типа контента. Имя нового типа контента задается в качестве имени предыдущего типа контента.
Вызов метода GetContentTypeByName для поиска соответствующего типа контента на сайте.
Если тип контента уже существует, никаких дальнейших действий не требуется, и при вызове метода return управление передается обратно в Main.
Если тип контента не существует, свойства типа контента задаются с помощью объекта ContentTypeCreationInformation с именем newCt.
Новый идентификатор типа контента назначается newCt.Id с помощью идентификатора базового типа контента документа 0x0101. Дополнительные сведения см. в статье Base Content Type Hierarchy.
Добавление нового типа контента с помощью 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 создает столбец сайта следующим образом:
Создание константы с именем fieldName для хранения имени поля. Имя нового поля задается именем предыдущего поля.
Получение столбцов сайта, определенных на сайте, с помощью свойства Web.Fields .
Поиск соответствующего поля на сайте путем сопоставления имен полей на сайте с 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 создает соединение между типом контента и полем:
Загрузка типа контента, а затем поле ссылается на этот тип контента с помощью свойства ContentType.FieldLinks .
Загрузка поля.
Определение того, ссылается ли тип контента на поле, используя contentType.FieldLinks.Any(f => f.Name == fieldName) для сопоставления имени поля.
Если тип контента уже ссылается на поле, никаких дальнейших действий не требуется, и при вызове метода return управление передается обратно в Main. Если тип контента не ссылается на поле, свойства ссылки на поле задаются для объекта FieldLinkCreationInformation .
Добавление объекта 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) на:
Назначение константе идентификатора старого типа контента.
Получение нового типа контента с помощью GetContentTypeByName.
Получение всех списков на сайте с помощью Web.Lists.
Загрузка всех списков на сайте и всех типов контента в каждом списке с помощью cc.Load(lists, l => l.Include(list => list. ContentTypes).
Для каждого возвращаемого списка выполняется поиск типов контента списка в соответствии с типом контента со старым идентификатором типа контента с помощью списка. ContentTypes.Any(c => c.StringId.StartsWith(oldContentTypeId))). Если совпадение найдено, список со старым типом контента добавляется в listsWithContentType.
Для каждого списка в спискахWithContentType:
Определение того, присоединен ли новый тип контента к списку. Если новый тип контента не присоединен к списку, используйте ContentTypeCollection.AddExistingContentType , чтобы присоединить новый тип контента к списку.
Получение всех элементов списка.
Для каждого элемента списка получение идентификатора типа контента элемента списка. Определите, равен ли идентификатор типа контента элемента списка старому идентификатору типа контента. Если идентификаторы типов контента не равны, перейдите к следующему элементу списка. Если идентификаторы типов контента равны, используйте ContentType.StringId , чтобы назначить новый идентификатор типа контента элементу списка.
Примечание.
Старый тип контента по-прежнему находится в списке, но он больше не используется. Теперь вы можете удалить старый тип контента из списков, а затем отозвать его. В этой статье описывается замена только типов контента документов. Если вы заменяете типы контента в макетах страниц, обязательно обновите свойство 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();
}
}