Pergunta

Atualmente tenho uma página mestra personalizada que está sendo implantada no escopo do Site.Depois de implantado e o recurso ativado, a página mestra fica disponível para uso.Consigo ativar o recurso e definir a página mestra em sites e os subsites herdam a página mestra corretamente.

O problema ocorre quando tento desativar o recurso.

Uma vez desativado, tenho um receptor de eventos que encontra todos os sites que possuem a página mestra do recurso definida e a define de volta para a página mestra padrão.Isso também inclui a configuração da página mestra em sites herdados.

Isso funciona bem, mas depois de concluído, tento excluir a página mestra da galeria de páginas mestras e ocorre um erro informando que ela ainda está em uso.Quando verifico os sites por meio da GUI, todos eles foram transferidos para Seattle e os sites herdados ainda estão herdando do pai, então tudo parece bem nesse sentido.

Quando eu entro no Conteúdo e Estrutura do conjunto de sites e procurar as páginas relacionadas à página mestra, ainda há um relacionamento com o _DeviceChannelMappings.aspx páginas para todos os sites que herdam a página mestra do pai.Veja abaixo:

Master Page Relationships

Não consegui encontrar uma maneira de remover programaticamente esse relacionamento e, por isso, não consigo excluir a página mestra da biblioteca de catálogos.

Se eu for manualmente para o conjunto de sites raiz na GUI e verificar o Redefina todos os subsites para herdar esta configuração de página mestra do site usando a página mestre de Seattle, os relacionamentos desaparecerão e o arquivo poderá ser excluído.

Qualquer ajuda seria apreciada.Aqui está meu código atual para a desativação do recurso:

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
    {
        using (SPSite siteCollection = (SPSite)properties.Feature.Parent)
        {
            string defaultMasterUrl = SPUrlUtility.CombineUrl(siteCollection.ServerRelativeUrl.EndsWith("/") ? siteCollection.ServerRelativeUrl : siteCollection.ServerRelativeUrl + "/" , "_catalogs/masterpage/seattle.master");
            string pulseMasterUrl = SPUrlUtility.CombineUrl(siteCollection.ServerRelativeUrl.EndsWith("/") ? siteCollection.ServerRelativeUrl : siteCollection.ServerRelativeUrl + "/" , "_catalogs/masterpage/pulse.v01.master");
            foreach (SPWeb web in siteCollection.AllWebs)
            {
                Hashtable hash = web.AllProperties;
                if (hash["__InheritsMasterUrl"].ToString() == "True" && !web.IsRootWeb)
                {
                    web.MasterUrl = web.ParentWeb.MasterUrl;
                    web.Update();
                }
                else if (web.MasterUrl == pulseMasterUrl)
                {
                    web.MasterUrl = defaultMasterUrl;
                    web.Update();
                }

                if (hash["__InheritsCustomMasterUrl"].ToString() == "True" && !web.IsRootWeb)
                {
                    web.CustomMasterUrl = web.ParentWeb.CustomMasterUrl;
                    web.Update();
                }
                else if (web.CustomMasterUrl == pulseMasterUrl)
                {
                    web.CustomMasterUrl = defaultMasterUrl;
                    web.Update();
                }
            }

            foreach (SPWeb web in siteCollection.AllWebs)
            {
                try
                {
                    SPFile file = web.GetFile(pulseMasterUrl);
                    if (file.Exists)
                    {
                        file.Delete();
                    }
                    file.Update();
                }
                catch (Exception ex)
                {
                }
            }
        }
    }
Foi útil?

Solução 2

Não sei se esta é a melhor maneira de corrigir esse problema, mas consegui resolvê-lo editando manualmente o __DeviceChannelMappings.aspx no Desativação de recurso.Isso liberará os mapeamentos para a página mestra personalizada e permitirá que ela seja excluída.

Aqui está o código final:

public class PulseMasterPageEventReceiver : SPFeatureReceiver
{
    public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
    {
        using (SPSite siteCollection = (SPSite)properties.Feature.Parent)
        {
            string defaultMasterUrl = SPUrlUtility.CombineUrl(siteCollection.ServerRelativeUrl.EndsWith("/") ? siteCollection.ServerRelativeUrl : siteCollection.ServerRelativeUrl + "/" , "_catalogs/masterpage/seattle.master");
            string pulseMasterUrl = SPUrlUtility.CombineUrl(siteCollection.ServerRelativeUrl.EndsWith("/") ? siteCollection.ServerRelativeUrl : siteCollection.ServerRelativeUrl + "/" , "_catalogs/masterpage/pulse.v01.master");

            foreach (SPWeb web in siteCollection.AllWebs)
            {
                web.AllowUnsafeUpdates = true;
                Hashtable hash = web.AllProperties;
                if (hash["__InheritsMasterUrl"].ToString() == "True" && !web.IsRootWeb)
                {
                    web.MasterUrl = web.ParentWeb.MasterUrl;
                    web.Update();
                }
                else if (web.MasterUrl == pulseMasterUrl)
                {
                    web.MasterUrl = defaultMasterUrl;
                    web.Update();
                }

                if (hash["__InheritsCustomMasterUrl"].ToString() == "True" && !web.IsRootWeb)
                {
                    web.CustomMasterUrl = web.ParentWeb.CustomMasterUrl;
                    web.Update();
                }
                else if (web.CustomMasterUrl == pulseMasterUrl)
                {
                    web.CustomMasterUrl = defaultMasterUrl;
                    web.Update();
                }
                web.AllowUnsafeUpdates = false;
            }

            foreach (SPWeb web in siteCollection.AllWebs)
            {
                string deviceChannelMappings = SPUrlUtility.CombineUrl(web.Url.EndsWith("/") ? web.Url : web.Url + "/", "_catalogs/masterpage/__devicechannelmappings.aspx");
                SPFile dcmFile = web.GetFile(deviceChannelMappings);
                Stream dcmFileStream = dcmFile.OpenBinaryStream();
                Stream dcmFileWrite = new MemoryStream();

                using (StreamWriter sw = new StreamWriter(dcmFileWrite))
                using (StreamReader sr = new StreamReader(dcmFileStream))
                {
                    string line;
                    bool foundCorrection = false;
                    while ((line = sr.ReadLine()) != null)
                    {
                        if (line.Contains("pulse.v01.master"))
                        {
                            foundCorrection = true;
                            line = line.Replace("pulse.v01.master", "seattle.master");
                        }
                        sw.WriteLine(line);
                    }
                    sw.Flush();
                    if (foundCorrection)
                    {
                        if (dcmFile.CheckOutType != SPFile.SPCheckOutType.None)
                            dcmFile.UndoCheckOut();
                        if (dcmFile.RequiresCheckout)
                        {
                            dcmFile.CheckOut();
                            dcmFile.SaveBinary(dcmFileWrite);
                            dcmFile.CheckIn("Updated master page references to default.");
                        }
                        else
                        {
                            dcmFile.SaveBinary(dcmFileWrite);
                        }
                        if (dcmFile.Level == SPFileLevel.Draft)
                        {
                            dcmFile.Publish("Updated master page references to default.");
                        }
                        dcmFile.Update();
                    }
                }
            }

            foreach (SPWeb web in siteCollection.AllWebs)
            {
                try
                {
                    SPFile file = web.GetFile(pulseMasterUrl);
                    if (file.Exists)
                    {
                        file.Delete();
                    }
                    file.Update();
                }
                catch (Exception ex)
                {
                }
            }
        }
    }

Outras dicas

Parece que sua página mestra está sendo usada por canais de dispositivos em seus sites.A Microsoft não fornece uma API pública para visualizar ou modificar as páginas mestras configuradas para um canal de dispositivo.Dito isso, em meu livro (SharePoint 2013 WCM Advanced Cookbook: http://tinyurl.com/lutktay), tenho alguns exemplos de como visualizar e modificar as páginas mestras configuradas para canais de dispositivos.

Aqui está o código de exemplo C# para visualizar os canais do dispositivo:

namespace Code6587EN.Ch02.GetDeviceChannelMaps
{
    using Microsoft.SharePoint;
    using System;
    using System.Collections;
    using System.Reflection;

    /// <summary>
    /// Console Application to get the Device Channel mappings for each
    /// Site in a Site Collection
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            // Get the Site Collection in a Using statement
            using (var site = new SPSite("http://sharepoint/sitecollection"))
            {
                // Get the Mappings File type and constructor
                var typeMappingFile = Type.GetType("Microsoft.SharePoint.Publishing.Mobile.MasterPageMappingsFile, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");                
                var consMappingFile = typeMappingFile.GetConstructor(new Type[] {typeof(SPWeb), typeof(bool), typeof(SPWeb)});

                // Iterate through each Site in the Site Collection
                foreach (SPWeb web in site.AllWebs)
                {
                    // Ensure the Site exists
                    if (web.Exists)
                    {
                        // Get the Mapping File for the Site
                        var mappingFile = consMappingFile.Invoke(new object[] { web, false, null });

                        // Output the Default channel details
                        Console.WriteLine("");
                        Console.WriteLine("Site: " + web.Url);
                        Console.WriteLine("Device Channel: Default");
                        Console.WriteLine("Master Page: " + web.CustomMasterUrl);

                        // Get the mappings field from the Mapping File and cast as the IDictionary interface
                        var mappings = (IDictionary)typeMappingFile.GetField("mappings", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(mappingFile);

                        // Iterate through each key in the IDictionary
                        foreach (var key in mappings.Keys)
                        {
                            // Get the Master Page Url property from the mapping object
                            var mappingObject = mappings[key];
                            var masterUrl = (string)mappingObject.GetType().GetProperty("MasterPageUrl", BindingFlags.Instance | BindingFlags.Public).GetValue(mappingObject, null);

                            // Output the Channel details
                            Console.WriteLine("");
                            Console.WriteLine("Site: " + web.Url);
                            Console.WriteLine("Device Channel: " + key);
                            Console.WriteLine("Master Page: " + masterUrl);
                        }

                        // Dispose the Site object
                        web.Dispose();
                    }
                }
            }

            // Wait for a key to be pressed before closing the application
            Console.WriteLine("Press Any Key to Continue...");
            Console.Read();
        }
    }
}

E aqui está o código de exemplo C# para configurar os canais do dispositivo:

namespace Code6587EN.Ch02.ApplyMasterToChannel
{
    using Microsoft.SharePoint;
    using System;
    using System.Collections;
    using System.Reflection;

    /// <summary>
    /// Console Application to apply a Master Page to a Device Channel
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            // Get the Site Collection in a Using statement
            using (var site = new SPSite("http://sharepoint/sitecollection"))
            {
                // Get the Mappings File type and constructor
                var typeMappingFile = Type.GetType("Microsoft.SharePoint.Publishing.Mobile.MasterPageMappingsFile, Microsoft.SharePoint.Publishing, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c");                
                var consMappingFile = typeMappingFile.GetConstructor(new Type[] {typeof(SPWeb), typeof(bool), typeof(SPWeb)});

                // Get the root Site in a Using statement
                using (var web = site.RootWeb)
                {                   
                    // Get the Mapping File
                    var mappingFile = consMappingFile.Invoke(new object[] { web, false, null });

                    // Get the Mappings and cast to an IDictionary
                    var mappings = (IDictionary)typeMappingFile.GetField("mappings", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(mappingFile);

                    // Set the Master Page Url of the mapping object 
                    mappings["PowerShell"].GetType().GetProperty("MasterPageUrl", BindingFlags.Instance | BindingFlags.Public).SetValue(mappings["PowerShell"], "/_catalogs/masterpage/seattle.master", null);

                    // Set the updated Mappings on the Mappings file
                    typeMappingFile.GetField("mappings", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(mappingFile, mappings);

                    // Get and invoke the Update Single Channel method
                    var updateMethod = typeMappingFile.GetMethod("UpdateSingleChannel", BindingFlags.Instance | BindingFlags.Public, null, new Type[] { typeof(string) }, null);
                    updateMethod.Invoke(mappingFile, new object[] { "PowerShell" });
                }
            }

            // Wait for a key to be pressed before closing the application
            Console.WriteLine("Press Any Key to Continue...");
            Console.Read();
        }
    }
}

Com esses dois exemplos, você terá o que precisa para obter os canais de dispositivos de cada site e garantir que eles não estejam usando sua página mestra.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a sharepoint.stackexchange
scroll top