How to Deploy Mobile Channels on SharePoint 2013

SharePoint 2013 bietet neu Mobile Channels an, welche die Device-gerechte Darstellung der Seiten auf verschiedene Mobile-Plattformen gewährleisten. Die Funktionalität sowie die Anwendung der Device Channels kann im Internet beispielsweise unter (http://blogs.msdn.com/b/sharepointdev/archive/2013/05/07/optimizing-sharepoint-2013-websites-for-mobile-devices.aspx) nachgelesen werden.

Dieser Artikel beschreibt, wie Device Channels automatisiert in einem Feature deployed werden können.

Der Ansatz von Microsoft

Als Ausgangslage habe ich das Deployment der Device Channels im SmallBusinessWebsite Feature von Microsoft verwendet. Dieses bietet Deployment für Mobile-Device Channels:

Das Feature.xml im Ordner %ProgramFiles%\Common Files\microsoft shared\Web Server Extensions\15\TEMPLATE\FEATURES\SmallBusinessWebsite verlinkt fürs Deployment der mobilen Master-Pages aufs MobileChannel.xml File.

Das MobileChannel.xml File verlinkt dabei die Mobile-Device Typen (UserAgentSubstrings; z.B. iOS oder $FallbackMobileUserAgents;) mit eigens definierten Namen (ChannelAlias) und füllt diese Informationen in die Device Channels List:

In den SharePoint Settings finden sich diese Informationen unter Device Channels wieder (abweichend zur Xml-Definition oben wird im folgenden Bild der Namics Mobile Channel dargestellt):

Master Pages für Device Channels

Anhand des ChannelAlias werden die hier definierten User Agent Gruppierungen zu Master Pages verknüpft. Die Benutzerschnittstelle dafür sieht wie folgt aus
(abweichend zur Xml-Definition oben wird im folgenden Bild der Namics Mobile Channel dargestellt):

Die Masterpage selbst wird im SmallBusinessWebsite Feature über das gewohnte Module-Deployment in die Masterpage-Gallery eingespielt:

Offen bleibt in der Feature-Definition, wie die Zuweisung der Masterpage auf einen Device Channel vonstattengeht.

Zuweisung der Masterpage auf einen Device Channel

Im SharePoint 2013 geschieht diese Zuweisung der Masterpage auf einen Device Channel über das Mappings-File __DeviceChannelMappings.aspx. Dieses liegt im MasterPage Katalog unter _catalogs/masterpage/ und enthält die Mappings im XML Format:

Beim SmallBusinessWebsite Feature kommt gemäss Feature-Definition der Microsoft.SharePoint.Publishing.SmallBusinessWebsiteFeatureHandler Receiver von Microsoft zum Einsatz, welcher das Mappings-File für die Site anlegt und konfiguriert.

Aufgrund der Komplexität des SmallBusinessWebsiteFeatureHandlers, habe ich hier das PublishingMobile Feature analysiert. Dieses deployed ebenfalls Mobile Channels:

Microsoft verwendet beim PublishingMobile Feature den Microsoft.SharePoint.Publishing.Mobile.FeatureHandler Receiver. Nach einigen Nachforschungen stellte ich fest, dass für die Zuweisung der Masterpage auf einen Device Channel Microsoft-interne (d.h. internal) Klassen wie die Microsoft.SharePoint.Publishing.Mobile.MasterPageMappingsFile zum Einsatz kommen:

Dies erschwert das automatische Deployment der Mobile Channels, da die entsprechenden Features nachgebaut werden müssen. Wesentliche Punkte dabei sind in folgenden Methoden abgedeckt:

  • Microsoft.SharePoint.Publishing.Mobile.MappingsFile<T>.UpdateFile()
    Channel Mappings File in die Master Page Gallery eintragen.
  • Microsoft.SharePoint.Publishing.Mobile.MappingsFile.SaveNoMobileMappingsInfoToRootWeb()
    Root Web Property für die Mobile Channels Deaktivierung (__NoMobileMapping) entfernen.

Putting it all together

Der folgende Code enthält den Receiver als Code-Schnippsel:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
/// <summary>
/// Initializes a new instance of the Microsoft.SharePoint.SPFeatureReceiver class.
/// </summary>
public class MobileChannelReceiver : SPFeatureReceiver
{
    private static readonly SPContentTypeId DeviceChannelMappingsContentTypeId = new SPContentTypeId("0x010100FDA260FD09A244B183A666F2AE2475A6");

    private const string mDeviceChannelFile = "__DeviceChannelMappings.aspx";
    private const string mMasterPageLibPath = "_catalogs/masterpage/";
    private const string mSiteCollectionUrlStart = @"href=""";
    private const string mSiteCollectionUrlReplacement = mSiteCollectionUrlStart + "~sitecollection";

    /// <summary>
    /// Deploys the mobile channel file into the Master Page Gallery. The existing
    /// file will be replaced with the file from the current feature.
    /// </summary>
    /// <param name="properties">An SPFeatureReceiverProperties object that represents the properties of the event.</param>
    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        using (SPWeb web = ((SPSite) properties.Feature.Parent).OpenWeb())
        {
            RegisterChannelFile(
                web,
                properties.Definition.RootDirectory + @"" + mDeviceChannelFile,
                mDeviceChannelFile);
        }
    }


    private void RegisterChannelFile(SPWeb rootWeb, string fullFilePathOfNewFile, string masterPageFileNameToReplace)
    {
        SPFile fileMasterpage = rootWeb.GetFile(mMasterPageLibPath + masterPageFileNameToReplace);

        if (fileMasterpage != null && fileMasterpage.Exists)
        {
            TryCheckedIn(fileMasterpage);
            TryCheckedOut(fileMasterpage);

            // patch file content with the root web url
            string mobileDeploymentFileRaw = File.ReadAllText(fullFilePathOfNewFile);
            string mobileDeploymentFile = mobileDeploymentFileRaw.Replace(
                mSiteCollectionUrlReplacement,
                mSiteCollectionUrlStart + rootWeb.ServerRelativeUrl);

            // Source: Microsoft.SharePoint.Publishing.Mobile.MappingsFile<T>::UpdateFile()
            // save patched content into server-side file library
            fileMasterpage.SaveBinary(Encoding.Unicode.GetBytes(mobileDeploymentFile));

            // update content type if has been set to a wrong one
            SPListItem item = fileMasterpage.Item;
               
            if (!DeviceChannelMappingsContentTypeId.IsParentOf((SPContentTypeId)item[SPBuiltInFieldId.ContentTypeId]))
            {
                item[SPBuiltInFieldId.ContentTypeId] = DeviceChannelMappingsContentTypeId;
                item.Update();
            }
               
            // accept changes and checkin
            fileMasterpage.CheckIn(string.Empty, SPCheckinType.MajorCheckIn);

            // approve if required
            SPModerationInformation moderationInformation = fileMasterpage.Item.ModerationInformation;

            if (moderationInformation != null && moderationInformation.Status == SPModerationStatusType.Pending)
            {
                fileMasterpage.Approve(string.Empty);
            }

            // activate custom master page channel mappings on web level
            // Source: Microsoft.SharePoint.Publishing.Mobile.MappingsFile<T>::SaveNoMobileMappingsInfoToRootWeb()
            string noMobileMappingKey = GetNoMobileMappingKey(rootWeb);
            if (rootWeb.AllProperties.ContainsKey(noMobileMappingKey))
            {
                rootWeb.AllProperties.Remove(noMobileMappingKey);
                rootWeb.Update();
            }
        }
    }

    private static string GetNoMobileMappingKey(SPWeb web)
    {
        return string.Format(CultureInfo.InvariantCulture, "__NoMobileMapping{0}", new object[]
        {
            web.ID.ToString("N")
        });
    }

    private bool TryCheckedOut(SPFile toCheckOut)
    {
        try
        {
            if (toCheckOut.Level != SPFileLevel.Checkout)
            {
                toCheckOut.CheckOut();
            }
        }
        catch (Exception ex)
        {
            LOG.Current.TraceUnexpected(
                CommonLibErrorCodes.EnsureApplicationFeatureReceiver,
                () => ex.Message);
            return false;
        }
        return true;
    }

    private bool TryCheckedIn(SPFile toCheckIn)
    {
        try
        {
            if (toCheckIn.CheckOutType != SPFile.SPCheckOutType.None)
            {
                toCheckIn.CheckIn("checked in by feature activation");
            }
            return false;
        }
        catch (Exception ex)
        {
            LOG.Current.TraceUnexpected(
                CommonLibErrorCodes.EnsureApplicationFeatureReceiver,
                () => ex.Message);
        }
        return true;
    }
}

Dafür wird das __DeviceChannelMappings.aspx Mappings File wie folgt deployed:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%@ Reference VirtualPath="~CustomMasterUrlForMapping0" %><%@ Reference VirtualPath="~CustomMasterUrlForMapping1" %><%@ Page Language="C#" Inherits="Microsoft.SharePoint.Publishing.Internal.WebControls.MappingsFileBasePage" %><html xmlns:mso="urn:schemas-microsoft-com:office:office" xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"><%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<head>
<!--[if gte mso 9]><SharePoint:CTFieldRefs runat=server Prefix="mso:" FieldList="FileLeafRef"><xml>
<mso:CustomDocumentProperties>
<mso:ContentTypeId msdt:dt="string">0x010100FDA260FD09A244B183A666F2AE2475A6</mso:ContentTypeId>
</mso:CustomDocumentProperties>
</xml></SharePoint:CTFieldRefs><![endif]-->
</head><body><mappings>
  <mapping>
    <channelAlias>NamicsMobileChannel</channelAlias>
    <masterUrl href="~sitecollection/_catalogs/masterpage/SP2013Mobile.Master" token="~sitecollection/_catalogs/masterpage/SP2013Mobile.master" />
  </mapping>
  <defaultChannelMapping>
    <siteMasterUrl token="~sitecollection/_catalogs/masterpage/SP2013.Master" href="~sitecollection/_catalogs/masterpage/SP2013.Master" />
    <systemMasterUrl token="~sitecollection/_catalogs/masterpage/seattle.master" href="~sitecollection/_catalogs/masterpage/seattle.master" />
    <alternateCssUrl token="" href="" />
    <themedCssFolderUrl token="" href="" isthemeshared="false" />
  </defaultChannelMapping>
</mappings></body></html>

Fazit

SharePoint 2013 bietet mit den neuen Mobile Channels einen effizienten Weg, um die Seite Mobile-tauglich zu gestalten. Das automatisierte Deployment via Features ist im Moment noch nicht durchgehend gelöst, da die API fast ausschliesslich als internal markiert ist.  Hier muss Hand angelegt und mit einem entsprechenden Feature Receiver die Deployment-Automatisierung nachgerüstet werden.

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>