Online-Befragung zu Unternehmensportalen und Intranets auf SharePoint

Gerne möchten wir euch heute auf eine Online-Befragung zum Thema „Unternehmensportale und Intranets mit Fokus auf Microsoft SharePoint“ von der Hochschule der Medien Stuttgart aufmerksam machen:

„Die Verbreitung der Portalsoftware Microsoft SharePoint ist in den letzten Jahren enorm gestiegen. Die Frage ist – wie zufrieden sind die Anwender? Dem geht die Hochschule der Medien Stuttgart im Zuge einer Befragung nach. Ziel ist es, anhand tatsächlicher Erfahrungen, Rückschlüsse auf die Nutzung und den Nutzen von Intranet und Unternehmensportalen bilden zu können. (mehr …)

SharePoint in Office 365 – Site Collection übergreifend Daten anziehen

In den meisten SharePoint Projekten – sei es on-premise oder in der Cloud – kommt man früher oder später an den Punkt, an dem Daten über Site Collections hinweg angezogen werden sollen. Diese Grenzen sind in on-premise Lösungen mit dem Objektmodel (SPSite, SPWeb, SPList, usw.) relativ einfach zu umgehen. In der Cloud und mittels clientseitigen SharePoint Technologien (z.B. mit CSOM) hingegen kann dies auf den ersten Blick nicht ganz so einfach realisiert werden. Doch mit der ausgebauten REST-Api in SharePoint 2013 ist dies trotzdem auf einfache und effiziente Art und Weise möglich. In einem kleinen Beispiel möchte ich aufzeigen, wie mittels einfachem REST Call Websites Site Collection übergreifend angezogen werden können. (mehr …)

TypeScript SharePoint 2013 App

SharePoint-hosted Apps für SharePoint 2013 schreibt man bekanntlich vermehrt in JavaScript. Da für JavaScript Sprachkonstrukte wie Klasse, Interfaces, Vererbung oder Module nicht existieren, ist es für .NET Entwickler vielfach nicht ganz einfach JavaScript Anwendungen richtig zu schreiben. Um diese Probleme in der Frontend Entwicklung zu eliminieren, hat Microsoft die Programmiersprache TypeScript entwickelt. TypeScript stellt einen Compiler zur Verfügung, welcher TypeScript Code in JavaScript Code umwandelt. Ebenso existiert ein Visual Studio Plugin, welches dem Entwickler IDE Support für TypeScript gibt. Eine Anleitung für die Installation ist auf der offiziellen Website von TypeScript beschrieben.
(mehr …)

RazorEngine in SharePoint 2013 nutzen

Da SharePoint 2013 auf dem .NET Framework 4 (oder 4.5) läuft, kann nun auch die OpenSource Templating Engine Razor in SharePoint Projekte eingebunden werden. SharePoint an sich basiert zwar weiterhin auf den bewährten ASP.NET WebForms, doch können Custom WebParts oder Controls sehr einfach auf die RazorEngine zugreifen.

In diesem Blogpost soll ein einfaches WebPart erstellt werden, welches aus einer SharePoint Liste alle Titel der Elemente ausliest und ausgibt.

Ich erstelle also ein neues SharePoint 2013 Projekt in Visual Studio und füge via NuGet Manager die Razor Engine der Solution hinzu. Anschliessend müssen unbedingt die beiden Assemblies (RazorEngine.dll und System.Web.Razor.dll) im SharePoint Package als additional Assemblies für den GlobalAssemblyCache hinzugefügt werden.

nuget

 

Als nächstes erstelle ich ein neues WebPart in der Solution. Dem WebPart wird ein Property für das Razor Template hinzugefügt. In dieses WebPart Property kann man später sein Model als WebPart Property bearbeiten.

[WebBrowsable(true),
WebDisplayName("TemplateModel"),
WebDescription("TemplatModel Property"),
Personalizable(PersonalizationScope.Shared),
Category("Template")]
public string TemplateModel { get; set; }

Ebenso überschreibe ich die Render Methode, um darin die Parse Methode von der Razor Engine aufzurufen. In meiner Render Methode werden statische Werte geladen, aber auch alle Titel der Seiten in der Pages Library des aktuellen SPWebs geladen.

protected override void Render(HtmlTextWriter writer)
{
// irgendwelche statischen Werte
const string title = "Pages Titles";
var pagesList = SPContext.Current.Web.Lists["Pages"].Items;
var pages = (from SPListItem page in pagesList select page.Title).ToList();
// erstelle ein Model (hier als anonyme Klasse gelöst)
var model = new { Data = title, Pages = pages };
// Parse das Model wenn ein Model vorhanden ist oder geb eine Fehlermeldung aus
if(!string.IsNullOrEmpty(TemplateModel))
{
var parsedHtml = Razor.Parse(TemplateModel, model);
// und der Output wird gerendert
writer.Write(parsedHtml);
}
else
{
writer.Write("Definiere ein Model in den WebPart Properties");
}
}

Nachdem die Solution deployed wurde, kann das WebPart auf einer Page hinzugefügt und editiert werden. Hier wird nun mittels RazorTemplate auf das im Code mitgegebene Model zugegriffen.

editwebpart

 

Wird das WebPart anschliessend gespeichert, rendert es folgenden Output auf die Seite.

webpart

Dieses kleine Beispiel zeigt, wie einfach es ist, mittels RazorEngine WebParts für SharePoint 2013 zu entwickeln.

 

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.

Seite 1 von 41234