Reemplazar listas de SharePoint creadas a partir de definiciones de lista en soluciones de granja de servidores
Si usó definiciones de lista para crear listas en la solución de granja de servidores, obtenga información sobre cómo transformarlas en nuevas soluciones que proporcionan una funcionalidad similar mediante el modelo de objetos de cliente (CSOM).
Importante
Las soluciones de granja no se pueden migrar a SharePoint Online. Al aplicar las técnicas y el código descrito en este artículo, puede crear una nueva solución con funciones similares a las que proporcionan las soluciones de granja de servidores, que pueden implementar después en SharePoint Online. Si usa un enfoque de transformación local, es posible que tenga que implementar la nueva solución en SharePoint Online. Si usa el enfoque swing o de migración de contenido, las herramientas de terceros pueden crear las listas automáticamente. Para obtener más información, vea Enfoques de transformación para implementar el nuevo complemento de SharePoint.
El código de este artículo requiere código adicional para ofrecer una solución que funcione completamente. Por ejemplo, en este artículo no se explica cómo autenticar Office 365, cómo implementar excepciones necesarias, etc. Para obtener ejemplos de código adicionales, consulte el Proyecto de patrones y prácticas para desarrolladores de Office 365.
Nota:
El código de este artículo se proporciona tal cual, sin garantía de ningún tipo, expresa o implícita, incluidas las garantías implícitas de aptitud para un propósito particular, comerciabilidad o ausencia de infracción.
Sugerencia
Use las técnicas descritas en este artículo para actualizar unas pocas listas a la vez. Además, al buscar listas para actualizar, no debe filtrar las listas por tipo de lista.
Para reemplazar listas creadas a partir de definiciones de lista mediante CSOM:
Busque listas creadas con una plantilla base específica.
Cree la nueva lista con la definición de lista integrada.
Configuración de la lista.
Establezca tipos de contenido en la nueva lista en función de los tipos de contenido establecidos en la lista original.
Agregar o quitar vistas (opcional).
Migre el contenido de la lista original a la nueva lista.
Antes de empezar
Lo ideal es revisar las soluciones de granja de servidores existentes, obtener información sobre las técnicas descritas en este artículo y, a continuación, planear cómo aplicar estas técnicas a las soluciones de granja de servidores existentes. Si no está familiarizado con las soluciones de granja de servidores o no tiene una solución de granja de servidores existente con la que trabajar, puede resultarle útil:
Revise Reemplazo de listas creadas a partir de plantillas personalizadas para obtener una descripción rápida de cómo se crearon las listas de forma declarativa mediante definiciones de lista.
Nota:
En Contoso.Intranet, en el archivo elements.xml de SP\ListTemplate\LTContosoLibrary, el identificador de la plantilla de lista personalizada es 10003. Usará esto para identificar la plantilla que se usó para crear la lista. Revise también los valores de configuración y las vistas que se definen en la lista.
Obtenga información sobre las soluciones de granja de servidores. Para obtener más información, vea Compilar soluciones de granja de servidores en SharePoint.
Búsqueda de listas creadas con una plantilla base específica
En el código siguiente, el método muestra cómo buscar listas creadas mediante una plantilla base específica. Este método:
Usa ClientContext para obtener todas las listas de la web actual mediante Web.Lists. La instrucción Include se usa en la expresión lambda para devolver una colección de listas. Las listas devueltas deben tener valores de propiedad especificados para BaseTemplate, BaseType y Title.
Para cada lista de la colección de listas devueltas, si List.BaseTemplate es igual a 10003, agrega la lista a una colección de listas que se van a reemplazar, denominada listsToReplace. Recuerde que 10003 era el identificador de la plantilla de lista personalizada que hemos revisado en el ejemplo Contoso.Intranet.
static void Main(string[] args) { using (var clientContext = new ClientContext("http://w15-sp/sites/ftclab")) { Web web = clientContext.Web; ListCollection listCollection = web.Lists; clientContext.Load(listCollection, l => l.Include(list => list.BaseTemplate, list => list.BaseType, list => list.Title)); clientContext.ExecuteQuery(); var listsToReplace = new List<List>(); foreach (List list in listCollection) { // 10003 is the custom list template ID of the list template you're replacing. if (list.BaseTemplate == 10003) { listsToReplace.Add(list); } } foreach (List list in listsToReplace) { ReplaceList(clientContext, listCollection, list); } } }
Importante
En el código anterior, primero recorre en iteración ListCollection para seleccionar qué listas deben modificarse y, a continuación, llame a ReplaceList, que comienza a modificar las listas. Este patrón es necesario porque al modificar el contenido de una colección mientras se itera por la colección se produce una excepción.
Después de identificar una lista que se debe reemplazar, ReplaceList muestra el orden de las operaciones que se deben realizar para reemplazar la lista.
private static void ReplaceList(ClientContext clientContext, ListCollection listCollection, List listToBeReplaced) { var newList = CreateReplacementList(clientContext, listCollection, listToBeReplaced); SetListSettings(clientContext, listToBeReplaced, newList); SetContentTypes(clientContext, listToBeReplaced, newList); AddViews(clientContext, listToBeReplaced, newList); RemoveViews(clientContext, listToBeReplaced, newList); MigrateContent(clientContext, listToBeReplaced, newList); }
Crear una lista
Para crear una nueva lista, CreateReplacementList usa ListCreationInformation.
El título de la nueva lista se establece en el título de la lista existente, con add-in anexado a ella. La enumeración ListTemplateType se usa para establecer el tipo de plantilla de la lista en una biblioteca de documentos. Si va a crear una lista basada en un tipo de plantilla diferente, asegúrese de usar el tipo de plantilla correcto. Por ejemplo, si va a crear una lista de calendarios, use ListTemplateType.Events en lugar de ListTemplateType.DocumentLibrary.
private static List CreateReplacementList(ClientContext clientContext, ListCollection lists,List listToBeReplaced)
{
var creationInformation = new ListCreationInformation
{
Title = listToBeReplaced.Title + "add-in",
TemplateType = (int) ListTemplateType.DocumentLibrary,
};
List newList = lists.Add(creationInformation);
clientContext.ExecuteQuery();
return newList;
}
Configuración de la lista
SetListSettings aplica la configuración de lista original a la nueva lista mediante:
Obtener varias opciones de configuración de lista de la lista original.
Aplicar la configuración de lista de la lista original a la nueva lista.
private static void SetListSettings(ClientContext clientContext, List listToBeReplaced, List newList)
{
clientContext.Load(listToBeReplaced,
l => l.EnableVersioning,
l => l.EnableModeration,
l => l.EnableMinorVersions,
l => l.DraftVersionVisibility );
clientContext.ExecuteQuery();
newList.EnableVersioning = listToBeReplaced.EnableVersioning;
newList.EnableModeration = listToBeReplaced.EnableModeration;
newList.EnableMinorVersions= listToBeReplaced.EnableMinorVersions;
newList.DraftVersionVisibility = listToBeReplaced.DraftVersionVisibility;
newList.Update();
clientContext.ExecuteQuery();
}
Nota:
En función de sus requisitos, la configuración de la lista de las listas originales puede ser diferente. Revise la configuración de la lista y cambie SetListSettings para asegurarse de que la configuración de la lista original se aplica a las nuevas listas.
Establecer tipos de contenido en la nueva lista
SetContentTypes establece los tipos de contenido de la nueva lista mediante:
Obtención de información de tipo de contenido de la lista original.
Obtención de información de tipo de contenido de la nueva lista.
Determinar si la lista original habilita los tipos de contenido. Si la lista original no habilita los tipos de contenido, Se cierra SetContentTypes . Si los tipos de contenido habilitados para la lista original, SetContentTypes habilita los tipos de contenido de la nueva lista mediante newList.ContentTypesEnabled = true.
Para cada tipo de contenido de la lista original, busque en los tipos de contenido de la nueva lista un tipo de contenido coincidente basado en el nombre del tipo de contenido mediante newList.ContentTypes.Any(ct => ct. Name == contentType.Name). Si el tipo de contenido no está en la nueva lista, se agrega a la lista.
Cargar newList por segunda vez porque AddExistingContentType podría haber cambiado los tipos de contenido.
Para cada tipo de contenido de newList, determinar si el tipo de contenido coincide con un tipo de contenido de la lista original según ContentType.Name mediante listToBeReplaced.ContentTypes.Any(ct => ct. Name == contentType.Name). Si no se encuentra una coincidencia, el tipo de contenido se agrega a contentTypesToDelete para eliminarse de la nueva lista.
Para eliminar el tipo de contenido, llame a ContentType.DeleteObject.
Nota:
Si usa un enfoque de transformación local y los tipos de contenido se implementaron mediante declaración mediante Feature Framework, debe:
- Cree nuevos tipos de contenido.
- Establezca el tipo de contenido en los elementos de lista al migrar el contenido de la lista original a la nueva lista en MigrateContent.
private static void SetContentTypes(ClientContext clientContext, List listToBeReplaced, List newList)
{
clientContext.Load(listToBeReplaced,
l => l.ContentTypesEnabled,
l => l.ContentTypes);
clientContext.Load(newList,
l => l.ContentTypesEnabled,
l => l.ContentTypes);
clientContext.ExecuteQuery();
// If the original list doesn't use content types, do not proceed any further.
if (!listToBeReplaced.ContentTypesEnabled) return;
newList.ContentTypesEnabled = true;
newList.Update();
clientContext.ExecuteQuery();
foreach (var contentType in listToBeReplaced.ContentTypes)
{
if (!newList.ContentTypes.Any(ct => ct.Name == contentType.Name))
{
// Current content type needs to be added to the new list. Note that the content type is added to the list, not the site.
newList.ContentTypes.AddExistingContentType(contentType.Parent);
newList.Update();
clientContext.ExecuteQuery();
}
}
// Reload the content types on newList because they might have changed when AddExistingContentType was called.
clientContext.Load(newList, l => l.ContentTypes);
clientContext.ExecuteQuery();
// Remove any content types that are not needed.
var contentTypesToDelete = new List<ContentType>();
foreach (var contentType in newList.ContentTypes)
{
if (!listToBeReplaced.ContentTypes.Any(ct => ct.Name == contentType.Name))
{
// Current content type needs to be removed from new list.
contentTypesToDelete.Add(contentType);
}
}
foreach (var contentType in contentTypesToDelete)
{
contentType.DeleteObject();
}
newList.Update();
clientContext.ExecuteQuery();
}
Nota:
En este punto, la nueva lista puede aceptar contenido de la lista original. Opcionalmente, también puede agregar y quitar vistas.
Agregar o quitar vistas (opcional)
Los usuarios pueden agregar o quitar vistas definidas en una lista para satisfacer las necesidades empresariales. Por este motivo, es posible que tenga que agregar o quitar vistas en la nueva lista.
Agregar vistas a la nueva lista
AddViews agrega vistas de la lista original a la nueva lista mediante:
Usar List.Views para obtener todas las vistas de la lista original.
Uso de la expresión lambda para cargar varias propiedades de vista en la colección views .
Para cada vista de la lista original, cree una vista mediante ViewCreationInformation.
Varias propiedades se establecen en la nueva vista en función de las propiedades de vista de la vista original. Se llama al método auxiliar GetViewType para determinar el ViewType de la vista. A continuación, la nueva vista se agrega a la lista de vistas denominadas viewsToCreate.
Agregar las vistas a la colección List.Views mediante el método Add de la colección Views de la lista.
private static void AddViews(ClientContext clientContext, List listToBeReplaced, List newList) { ViewCollection views = listToBeReplaced.Views; clientContext.Load(views, v => v.Include(view => view.Paged, view => view.PersonalView, view => view.ViewQuery, view => view.Title, view => view.RowLimit, view => view.DefaultView, view => view.ViewFields, view => view.ViewType)); clientContext.ExecuteQuery(); // Build a list of views which exist on the original list only. var viewsToCreate = new List<ViewCreationInformation>(); foreach (View view in listToBeReplaced.Views) { var createInfo = new ViewCreationInformation { Paged = view.Paged, PersonalView = view.PersonalView, Query = view.ViewQuery, Title = view.Title, RowLimit = view.RowLimit, SetAsDefaultView = view.DefaultView, ViewFields = view.ViewFields.ToArray(), ViewTypeKind = GetViewType(view.ViewType), }; viewsToCreate.Add(createInfo); } foreach (ViewCreationInformation newView in viewsToCreate) { newList.Views.Add(newView); } newList.Update(); } private static ViewType GetViewType(string viewType) { switch (viewType) { case "HTML": return ViewType.Html; case "GRID": return ViewType.Grid; case "CALENDAR": return ViewType.Calendar; case "RECURRENCE": return ViewType.Recurrence; case "CHART": return ViewType.Chart; case "GANTT": return ViewType.Gantt; default: return ViewType.None; } }
Quitar vistas de la nueva lista
En el código siguiente, RemoveViews quita las vistas de la nueva lista mediante:
Obtener las vistas de lista en la nueva lista mediante la propiedad List.Views .
Para cada vista de la nueva lista, determine si esa vista no existe en la lista original mediante la coincidencia en el título de la vista mediante la vista !listToBeReplaced.Views.Any(v => v.Title ==). Título. Si una vista de la nueva lista no tiene una vista coincidente en la lista original, agregue la vista a viewsToRemove.
Eliminar todas las vistas de viewsToRemove mediante View.DeleteObject.
private static void RemoveViews(ClientContext clientContext, List listToBeReplaced, List newList) { // Get the list of views on the new list. clientContext.Load(newList, l => l.Views); clientContext.ExecuteQuery(); var viewsToRemove = new List<View>(); foreach (View view in newList.Views) { if (!listToBeReplaced.Views.Any(v => v.Title == view.Title)) { // If there is no matching view in the source list, add the view to the list of views to be deleted. viewsToRemove.Add(view); } } foreach (View view in viewsToRemove) { view.DeleteObject(); } newList.Update(); clientContext.ExecuteQuery(); }
Migración de contenido de la lista original a la nueva lista
MigrateContent migra el contenido de la lista original a la nueva lista mediante:
Recuperar el destino en el que copiar los archivos o la carpeta raíz de la nueva lista mediante List.RootFolder. La dirección URL relativa al servidor de la carpeta de lista de destino se recupera mediante Folder.ServerRelativeUrl.
Recuperar el origen de los archivos o la carpeta raíz de la lista original mediante List.RootFolder. La dirección URL relativa al servidor de la carpeta de lista y todos los archivos de la carpeta raíz del origen se cargan mediante el objeto clientContext .
Para cada archivo del origen, creando newUrl, que almacena la nueva dirección URL del archivo. newUrl se crea reemplazando la carpeta raíz de origen por la carpeta raíz del destino.
Usar File.CopyTo para copiar el archivo en la dirección URL de la carpeta raíz de destino. Como alternativa, puede optar por usar el método File.MoveTo para mover el archivo a la dirección URL de destino.
Nota:
El código siguiente devuelve todos los elementos de lista. En el entorno de producción, considere la posibilidad de optimizar el código siguiente mediante la implementación de un bucle y el uso de varias iteraciones para migrar pequeñas cantidades de elementos de lista.
private static void MigrateContent(ClientContext clientContext, List listToBeReplaced, List newList)
{
ListItemCollection items = listToBeReplaced.GetItems(CamlQuery.CreateAllItemsQuery());
Folder destination = newList.RootFolder;
Folder source = listToBeReplaced.RootFolder;
clientContext.Load(destination,
d => d.ServerRelativeUrl);
clientContext.Load(source,
s => s.Files,
s => s.ServerRelativeUrl);
clientContext.Load(items,
i => i.IncludeWithDefaultProperties(item => item.File));
clientContext.ExecuteQuery();
foreach (File file in source.Files)
{
string newUrl = file.ServerRelativeUrl.Replace(source.ServerRelativeUrl, destination.ServerRelativeUrl);
file.CopyTo(newUrl, true);
//file.MoveTo(newUrl, MoveOperations.Overwrite);
}
clientContext.ExecuteQuery();
}
Nota:
El código anterior muestra cómo migrar archivos almacenados en la carpeta raíz de una lista. Si la lista tiene subcarpetas, debe agregar código adicional para migrar las subcarpetas y su contenido. Si la lista usa flujos de trabajo, se requiere código adicional para asociar el flujo de trabajo a la nueva lista.