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 …)

Display SharePoint 2013 Followed items in a WebPart

SharePoint 2013 bietet die Möglichkeit, verschiedenen Seiten und Dokumenten zu folgen. Hierfür wird die MySite benötigt, welche als Site Collection in der Central Administration angelegt wird. Die Details zum Setup hat Microsoft unter http://technet.microsoft.com/en-us/library/ee624362.aspx beschrieben.

Dieser Artikel beschreibt wie die Followed Sites aus SharePoint in einen eigenen WebPart integriert werden können.

Warten auf die SP.SOD Klasse
Sämtliche Features der MySite werden über JavaScript / AJAX Calls angeboten. Daher ist hier der erste Ansatz, sich mit den Client-Side SharePoint Komponenten auseinander zu setzen. Dabei fällt als erstes die SP.SOD Klasse auf: Diese bietet On-Demand JavaScripting an: Somit ist es möglich, Funktionalitäten im Nachhinein zu laden und auf dessen Bereitstellung zu warten:

// Make sure SP.ClientContext is available
SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function () {
    // Make sure SocialFollowingManager is available
    SP.SOD.executeFunc('userprofile', 'SP.Social.SocialFollowingManager', function() {
        // Do something when SocialFollowingManager is available
    });
});

Die executeFunc wartet, bis die angegebene Funktion (z.B. SP.ClientContext) geladen wurde und ruft anschliessend den bereitgestellten Funktionszeiger auf. Dieses Verhalten kann so verschachtelt werden, bis alle die benötigten Klassen mit Sicherheit geladen wurden.
Im Code-Schnippsel oben wird also gewartet, bis der SP.Social.SocialFollowingManager auf dem Client-Browser verfügbar ist.

Daten mit SP.Social.SocialFollowingManager Abfragen
Zum Abfragen der MySite  Daten wird die SocialFollowingManager Klasse verwendet. Auf dieser ist für unseren Case die getFollowed Methode interessant. Diese gibt als Resultat SocialActors zurück, welche die für uns relevanten Followed Sites enthalten. Aufgrund des SocialActorTypes kann bestimmt werden, welche Resultate (für uns Sites) im Result-Array gelistet werden.

Der folgende Code initialisiert den SocialFollowingManager und ruft anschliessend die getFollowed Methode auf:

var clientContext = SP.ClientContext.get_current();
var socialManager = new SP.Social.SocialFollowingManager(clientContext);
           
var socialActor = new SP.Social.SocialActorInfo();
socialActor.set_actorType(SP.Social.SocialActorTypes.sites);
           
var futureObject =  { result: socialManager.getFollowed(socialActor) };
     
clientContext.executeQueryAsync(
    function() {
        // visualize content in futureObject
    });

Zum Schluss wichtig ist der Aufruf von executeQueryAsync: Erst dieser führt den Call auf den Server aus und lädt die Daten vom Server ins futureObject.

Das WebPart als Ganzes
Anschliessend können die Daten mit beliebigem Client-seitigen Code visualisiert werden. Das Code-Schnippsel enthält ebenfalls den WebPart-Rumpf sowie die für ASP.NET benötigten Komponenten fürs Rendering.

[ToolboxData("<{0}:FavoritesWebPart runat=server></{0}:FavoritesWebPart>")]
public class FavoritesWebPart : System.Web.UI.WebControls.WebParts.WebPart
{
    #region Private Members

    private const string FavoritesContainerId = "fav";

    // Source:
    //  -> MSDN: http://msdn.microsoft.com/en-us/library/jj679862.aspx
    //  -> BLOG: http://techmikael.blogspot.ch/2013/03/following-or-favorite-pages-in.html
    //  -> BLOG: http://anujabhojani.blogspot.ch/2013_03_01_archive.html
    //  -> BLOG: http://blog.lekman.com/2013/06/making-suite-bar-menu-dropdown-in.html (SOLUTION)
    private const string FavoritesScriptStart = @"<script type=""text/javascript"">
<!--
(function($) {

(function(strElementId){

    var namicsFollower = {
        result: null,
        show: function() {
            var element = $("
"#"" + strElementId);
            $.each(this.result, function(idx, site) {
                var followCtnr = $("
"<p />"");
                var imageCtnr = $("
"<span />"",
                    {
                        style: "
"height:16px;width:16px;position:relative;display:inline-block;overflow:hidden;"",
                        'class': "
"s4-clust ms-promotedActionButton-icon""
                    });
                imageCtnr.append($("
"<img />"",
                    {
                        src: "
"/_layouts/15/images/spcommon.png?rev=23"",
                        alt: "
"Follow"",
                        style: "
"position: absolute; left: -218px; top: -48px;""
                    }));

                followCtnr.append(imageCtnr);
                followCtnr.append($("
"<a />"",
                    {
                        href: site.get_uri(),
                        text: site.get_name(),
                        style: "
"vertical-align: middle""
                    }));
                element.append(followCtnr);
            });
        },
        loadData: function() {
            var clientContext = SP.ClientContext.get_current();
            var socialManager = new SP.Social.SocialFollowingManager(clientContext);
       
            var socialActor = new SP.Social.SocialActorInfo();
            socialActor.set_actorType(SP.Social.SocialActorTypes.sites);
       
            this.result = socialManager.getFollowed(socialActor);

            clientContext.executeQueryAsync(Function.createDelegate(this, this.show));
        }
    }

    // Make sure SP.ClientContext is available
    SP.SOD.executeFunc('sp.js', 'SP.ClientContext', function () {
        // Make sure SocialFollowingManager is available
        SP.SOD.executeFunc('userprofile', 'SP.Social.SocialFollowingManager', function() {
            namicsFollower.loadData();
        });
    });
})("
;
    private const string FavoritesScriptInit = @"'{0}'";
    private const string FavoritesScriptEnd = @"
);

})(jQuery);

//-->
</script>"
;
   

    #endregion

    #region Public Members

    public FavoritesWebPart()
    {
    }

    /// <summary>
    /// Lifecycle override method for the CreateChildControls State of the WebPart.
    /// </summary>
    /// <remarks>
    /// The method handles all exceptions.
    /// </remarks>
    protected override void CreateChildControls()
    {
        try
        {
            base.CreateChildControls();

            HtmlControl favContainer = new HtmlGenericControl("div");
            favContainer.ID = FavoritesContainerId;
            Controls.Add(favContainer);

            Controls.Add(new LiteralControl(FavoritesScriptStart));
            Controls.Add(new LiteralControl(string.Format(
                FavoritesScriptInit,
                favContainer.ClientID)));
            Controls.Add(new LiteralControl(FavoritesScriptEnd));
        }
        catch (Exception ex)
        {
            // TODO: Log exception here...
            throw;
        }
    }

    #endregion
}

SharePoint – Frontend Integration mit Terrific – Teil 2

Wer auf dem Sitecore-Blog (sitecore.namics.com) den Artikel über die Terrific Integration in Sitecore gelesen hat, erinnert sich sicher an die Filehandler für das dynamische zusammenführen der JS- und CSS-Files. Für die SharePoint Projekte können wir diese Handler so in dieser Form leider nicht verwenden. Der Grund ist der, dass die Site nicht in einem inetpub-Folder oder sonst einem vergleichbaren Folder liegt, sondern tief im Filesystem – die Version SharePoint 2010 hat folgenden Default Pfad: Program Files/Common files/Microsoft Shared/Web Server Extensions/14. Aufgrund von Berechtigungen wäre es sehr aufwendig und problematisch gewesen, den bekannten Filehandler aus den Sitecore-Projekten zu verwenden um die CSS- und JS-Files zu schreiben. Anstelle der Handler wurde diese Funktionalität in den Buildprozess eingebaut. In einem Targets-File wurde ein Solution Builder integriert, als Beispiel:

<Exec Command="%22..\_Tools\Namics.Solution.Builder.exe%22 %22$(SolutionDir) /$(ConfigurationName)|$(Platform) /$(SolutionDir)_Deployment\$(SolutionName).cab /14 /true%22" />

Anstelle der beiden Filehandler für die CSS- und JS-Files wurde eine Klasse für das Lesen der Files (ReadFiles), Erstellen des CSS-Files (CreateCssFile) und des JS-Files (CreateJsFile). Das heisst dass es dann für die Layouts und Module (Controls, WebParts) jeweils nur 1 JS- und 1 CSS-File gibt.

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
public static class StyleHandler
{
public static string ReadFile(string pFilePath)
{
try
{
var sb = new StringBuilder();

// Open the file and read the content
using (var fs = new FileStream(pFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
using (var sr = new StreamReader(fs))
{
// Write the Filename as Comment before content.
sb.AppendFormat("/*READ FILE: {0} */\n ", pFilePath);
sb.AppendLine(sr.ReadToEnd());
}
}

return sb.ToString();
}
catch (Exception ex)
{
// Return error Message if an exception is thrown into the file
return "/* Error on Reading File: " + ex.Message + " */";
}
}

public static void CreateCssFile(string pSolutionPath, string pTargetFileName)
{
string sb = ReadFiles(pSolutionPath.TrimEnd('\') + "\\MasterTemplate\\" + Program.SharePointHive + "\\TEMPLATE\\Assets\\css\\", "*.css");
sb += ReadFiles(pSolutionPath.TrimEnd('
\') + "\\MasterTemplate\\" + Program.SharePointHive + "\\TEMPLATE\\Features\\", "*.css");
sb += ReadFiles(pSolutionPath.TrimEnd('
\') + "\\Controls\\Modules\\", "*.css");
sb += ReadFiles(pSolutionPath.TrimEnd('
\') + "\\WebParts\\Modules\\", "*.css");

var targetCssFileNameSplit = pTargetFileName.Split('
\');
var targetCss = targetCssFileNameSplit[targetCssFileNameSplit.Length - 1].Replace(".cab", ".css");
var targetCssPath = pSolutionPath.TrimEnd('
\') + "\\MasterTemplate\\" + Program.SharePointHive +
"\\TEMPLATE\\LAYOUTS\\" + targetCss.Replace(".css", ".MasterTemplate") + "\\Styles\\" + targetCss;
TextWriter tw = new StreamWriter(targetCssPath);
tw.Write(sb);
tw.Close();

TextWriter twMin = new StreamWriter(targetCssPath.Replace(".css", ".min.css"));
twMin.Write(Yahoo.Yui.Compressor.CssCompressor.Compress(sb));
twMin.Close();
}

public static void CreateJsFile(string pSolutionPath, string pTargetFileName)
{
string sb = ReadFiles(pSolutionPath.TrimEnd('
\') + "\\MasterTemplate\\" + Program.SharePointHive + "\\TEMPLATE\\Assets\\js\\integration\\src\\", "jquery*.min.js");
sb += ReadFiles(pSolutionPath.TrimEnd('
\') + "\\MasterTemplate\\" + Program.SharePointHive + "\\TEMPLATE\\Assets\\js\\integration\\src\\", "terrific*.js");
sb += ReadFiles(pSolutionPath.TrimEnd('
\') + "\\MasterTemplate\\" + Program.SharePointHive + "\\TEMPLATE\\Assets\\js\\libraries\\static\\", "*.js");
sb += ReadFiles(pSolutionPath.TrimEnd('
\') + "\\MasterTemplate\\" + Program.SharePointHive + "\\TEMPLATE\\Assets\\js\\plugins\\static\\", "*.js");
sb += ReadFiles(pSolutionPath.TrimEnd('
\') + "\\MasterTemplate\\" + Program.SharePointHive + "\\TEMPLATE\\Assets\\js\\utils\\static\\", "*.js");
sb += ReadFiles(pSolutionPath.TrimEnd('
\') + "\\MasterTemplate\\" + Program.SharePointHive + "\\TEMPLATE\\Features\\", "*.js");
sb += ReadFiles(pSolutionPath.TrimEnd('
\') + "\\Controls\\Modules\\", "*.js");
sb += ReadFiles(pSolutionPath.TrimEnd('
\') + "\\WebParts\\Modules\\", "*.js");

var targetJsFileNameSplit = pTargetFileName.Split('
\');
var targetJs = targetJsFileNameSplit[targetJsFileNameSplit.Length - 1].Replace(".cab", ".js");
var targetJsPath = pSolutionPath.TrimEnd('
\') + "\\MasterTemplate\\" + Program.SharePointHive +
"\\TEMPLATE\\LAYOUTS\\" + targetJs.Replace(".js", ".MasterTemplate") + "\\Scripts\\" + targetJs;
TextWriter tw = new StreamWriter(targetJsPath);
tw.Write(sb);
tw.Close();

TextWriter twMin = new StreamWriter(targetJsPath.Replace(".js", ".min.js"));
twMin.Write(Yahoo.Yui.Compressor.JavaScriptCompressor.Compress(sb));
twMin.Close();
}

private static string ReadFiles(string pFolder, string pPattern)
{
var sb = new StringBuilder();
var files = Directory.GetFiles(pFolder, pPattern, SearchOption.AllDirectories);
foreach (string file in files)
{
// Read content of the current file
if (!file.Contains("\\test\\"))
{
sb.AppendLine(ReadFile(file));
}
}
return sb.ToString();
}

}

 

Für den Frontend-Engineer ändert sich eigentlich nichts im Vergleich zur bisherigen Arbeitsweise. Er kann nach wie vor in seiner gewohnten Umgebung arbeiten und muss einzig darauf achten, ob es sich um ein WebPart oder ein Control handelt und entsprechend nach der Erstellung des Modules dieses verschieben. Der Software Engineer kann so auch einfach selber kleinere JS- und/oder CSS-Anpassungen vornehmen und einchecken. Es existiert nur ein Frontend-Stand und es man läuft nicht Gefahr, verschiedene und unterschiedliche Frontend-Stände während der Projektentwicklung zu haben.

Vorteile/Nachteile – Mehrwert für Projekt

Ein Vorteil für ein Projekt ist sicher der, dass es keine Missverständnisse mehr gibt, welches CSS, JS oder Frontendtemplate aktuell ist – denn es gibt nur einen Stand. Die HTML-Files, in unserem Falle die phtml-Templates sind so an der korrekten Stelle im Projekt und können dann direkt in ein UserControl umgewandelt werden. So benötigt der Software Engineer keine lokale Terrific Installation oder Zugriff auf den Terrific-Staging – so kann das Markup direkt aus dem phtml verwendet werden. Die Filehandler können beliebig erweitert werden bei Bedarf und können aus dem Solution-Builder für alle Projekte verwendet werden – somit besteht ein geringer Projektsetup.

Next Steps und Ausblick

Für unsere SharePoint Projekte werden wir prüfen, ob in Zukunft eine abgespeckte Terrific Version eingesetzt werden kann, diese nennt sich Terrific Micro und ist massiv schlanker als das ursprüngliche Terrific. Hierfür wurde ein „Frontend Handler 2.0“ entwickelt welchen wir demnächst präsentieren werden.

Vorinformation von Mark Lowe zu dieser überarbeiteten Version:

„Überarbeitete Version vom bewährten Terrific Handler (JS / CSS) mit Performance-Verbesserung, voller LESS Unterstützung + Terrific Micro Unterstützung.“

Funktionen:

–          Zusammenführen der verstreuten Files
–          Reihenfolge welche in einem JSON File definiert sind einhalten
–          Komprimieren
–          Caching Serverseitig
–          Browser Caching

SharePoint – Frontend Integration mit Terrific – Teil 1

Schon seit geraumer Zeit setzen wir für professionelle Frontendentwicklung Terrific oder seit letztem Jahr auch den Terrific Composer ein. Was ist Terrific in einem Satz?

Terrific basiert auf der Idee, das Frontend in wiederwendbare, modulare Lego-Bausteine aufzuteilen.

Eine detaillierte Beschreibung inkl. Downloads und Möglichkeit einen Teil der Features gleich auszuprobieren findet Ihr hier:  http://terrifically.org

History und Motivation

Beim Start von Terrific für die Frontendentwicklung gab es leider keine Integration in die entsprechende SharePoint VisualStudio Solution. Die Probleme sind da vorprogrammiert, denn die Frontend-Solution liegt nicht im selben Repository wie die SharePoint Solution. Weiter kommt da eine zusätzliche Schwierigkeit im Vergleich zum CMS „Sitecore“ hinzu: für SharePoint kann nicht wie bei einem Sitecore-Projekt einfach ein Modules Folder für alle Komponenten erstellt und via SVN Externals angezogen werden. Denn hier haben wir zusätzlich die Unterscheidung ob die Komponenten ein Control oder ein WebPart ist. Dies hat für die Integration bereits Anpassungen am Terrific-Core nötig gemacht.

Eine aktuellere Terrific Version wurde kopiert und im AssetController in 2 Funktionen (cssAction und jsAction) der Array für den Module-Pfad erweitert. In der Modulimplementation des Terrificprojektes wurde der Helper ModuleImpl.php angepasst (Rendering der Modul Templates)

 

1
2
3
4
5
6
7
8
if($this->configs['webpart']) {
//echo 'modules_webpart';
$templatePath = APPLICATION_PATH . '/public/modules_webpart/' . $this-&gt;name;
}
else {
//echo 'modules_control';
$templatePath = APPLICATION_PATH . '/public/modules_control/' . $this-&gt;name;
}

 

Weiter musste beim Initialisieren der Views eine Erweiterung vorgenommen werden:

 

1
2
$view->addScriptPath(APPLICATION_PATH."/public/modules_control");
$view->addScriptPath(APPLICATION_PATH."/public/modules_webpart");

 

Die Pfade und Angaben auf das Terrific-Menu wurde so belassen. Somit können nach wie vor die Module im Browser erstellt werden, danach muss von Hand vom Frontend-Engineer die Komponenten in den Folder „modules_control“ oder „modules_webpart“ verschoben werden. Diese beiden Foldern müssen vorgängig im Folder Terrific/public erstellt werden. Für den Aufruf in einer View muss nun entschieden werden, ob es sich um ein Webpart oder ein Control handelt. Bis jetzt hat ein einfacher Aufruf so ausgesehen:

<?= $this->module("homebanner") ?>

Neu für ein Webpart:

1
<?= $this->module("newslist", "", "", null, array('webpart' => true)) ?>

Control:
kann wie gehabt aufgerufen werden.
Die Paramter für den Modulaufruf:

1
<?= $this->module("ModuleName", Templatename"", Skin"", Connectors, Wen nein Wepbart, dann: array('webpart' => true)) ?>

Aufbau der VisualStudio-Solution

Eine mögliche Solution hat in etwa diese Folderstruktur:

Der Assets-Folder aus dem Terrific wird für die allgemeinen Elemente wie JavaScripte, CSS, Bilder usw. benötigt. Hierfür erstellen wir ein SVN External (wird immer auf dem übergeordneten Ordner erstellt) auf dem Folder TEMPLATE unter MasterTemplate/14/.

Local Path: Assets
URL: https://svn-url/TerrificComposerIntegration/SharePoint2010/Terrific/public/assets

Das Layout aus dem Terrific wird bei den MasterPages angelegt, z.Bsp. unter MasterTemplate/14/TEMPLATE/Features/Kunde.Layouts/MasterPages

Local Path: Layout
URL: https://svn-url/TerrificComposerIntegration/SharePoint2010/Terrific/public/layout

Integration der Module aus dem Terrific via SVN Externals. Auf dem Projektfolder für die Controls wird im Fileexplorer via Tortoise SVN, Properties, New…Externals, New der entsprechende Pfad auf das Terrific angegeben:
Local Path: Modules
URL: https://svn-url/TerrificComposerIntegration/SharePoint2010/Terrific/public/modules_control

Für die WebParts, welche in der SharePoint Solution an einer anderen Stelle liegen, sieht ein External in etwa wie folgt aus:

Local Path: Modules
URL: https://svn-url/TerrificComposerIntegration/SharePoint2010/Terrific/public/modules_webpart

Wichtig: als nächster Schritt muss auf der gesamten Solution ein SVN Update gemacht werden. Hier werden nun diese Folder in unserer Solution erstellt/hinzugefügt und anschliessend kann die Solution Commitet werden.

Im linken Screenshot im Projekt „Kunde.Controls“ finden wir die Module aus dem Terrific welche dann im SharePoint als Control umgesetzt werden. Im rechten Screenshot die Struktur des Projekts „Kunde.WebParts“.

 

SharePoint 2010 vs. SharePoint 2013: Vergleich der Beschränkungen – Grenzen

Microsoft hat auf Technet die Grenzen von SharePoint 2013 im folgenden Artikel veröffentlicht: Softwarebeschränkungen und -grenzen für SharePoint Server 2013 Preview

Wo und wie sind die Grenzen im Vergleich zu der Vorgängerversion 2010 verschoben worden?

Die Unterschiede sind in tabellarischer Form in  diesem Artikel von Noorez Khamis (MVP) übersichtlich dargestellt.

Allgemein kann gesagt werden, dass sich die Beschränkungen gleichen, es jedoch bei der Search Service Applikation Unterschiede gibt. So sind die Werte tendenziell nach unten korrigiert worden.

Ich denke aber, dass die Grenzen bei SharePoint 2010 einfach nicht ganz korrekt waren und dass man aus den Erfahrungen mit SharePoint 2010 die SharePoint 2013 Grenzen entsprechend angepasst hat.

Seite 1 von 512345