Deploy global resources in a SharePoint Farm

Mikhail Dikov has written a very good article about how to use and deploy global resources within SharePoint.
He uses a Feature Receiver and creates a one-time scheduled job which copies the resource files from the feature folder to the App_GlobalResources folder under Inetpub\wwwroot\wss\VirtualDirectories\[port].

Now, why I'm writing about this?

Because there is one little thing within his code, that makes this solution unusable for a farm with more than one WFE (Web front-end server).
The resource files were copied only on one WFE each time I activated the feature. After searching the web a while, I found that the parameter SPJobLockType is the problem.

In Mikhails solution this parameter is set to "Job", which defines that this job executes only on one machine at a time. In a farm, I want to execute this job on each WFE. So set this parameter to "None" and it works!

Another solution is the article from Maxime Bombardier "Deploying resource files across a farm". For me it seems to be a little bit difficult and more complex than the solution from Mikhail.

CMS mit SharePoint 2007 - Teil 1: Das SharePoint Variation System

Ich weiß, dass es einige Webseiten gibt, die insgesamt wahrscheinlich eine Menge Gründe nennen können, warum man Variations mit SharePoint 2007 nicht einsetzen sollte.

Im aktuellen Projekt war die Entscheidung für Variations bereits lange vor Beginn gefallen...

Daher ein paar nützliche Hinweise die man evtl. vorher beachten sollte oder die im Nachhinein vielleicht noch was retten können. ;-)

  1. Insgesamt ist das Variationsystem leider sehr instabil, auch mit installiertem SP1.
    Beispiel: Für die Migration von einem vorhandenen alten System haben wir u.a. ein Tool geschrieben, um aus Einträgen einer SharePoint Liste Sites und Pages auf einem Zielsystem zu erstellen. Sei es über as SP Objektmodell oder über stsadm, stellenweise und aus unerfindlichen Gründen knallt es beim Anlegen der Variations. Da die Sourcesprache aber (durch unseren code) (meistens) korrekt angelegt wird, ist der dahinterliegende Timerjob des Variationsystems das Problem.
    Eine wirkliche Lösung gibt es offensichtlich bisher nicht, daher führen wir das Erstellen der Sites und Pages nun verzögert aus, um jeweils nach ein paar Sites erstmal auf den SharePoint Timerjob zu warten...
  2. Variations per SharePoint Solution/Feature deployen bereitete uns ebenfalls einige Kopfschmerzen.
    Die Labels der Variationen die man anlegen möchte, lassen sich noch relativ leicht über ein paar Zeilen code in einem Feature Receiver anlegen. Um nun die Hierarchien zu erstellen, kann man entweder über die GUI gehen und auf den Button "Create Hierarchies" klicken oder man macht auch das per Code. Sinnvoll ist es, damit man später das ganze wirklich ohne manuelle Eingriffe von einem Administrator installieren lassen kann. Leider will Microsoft das wohl nicht, denn die zugehörige Klasse, bzw. Methode ist als internal deklariert und somit nicht verfügbar. Da das inakzeptabel ist, half nur ein Artikel von Codeplex, in dem beschrieben wird, wie man mittels Reflection doch noch zum gewünschten Ergebnis kommt. Und ja, die Alternative die Seite per WebRequest aus dem Code raus anzustoßen und vorher entsprechend zu manipulieren funktioniert nur bedingt und natürlich schon gar nicht zusammen mit Mehrsprachigkeit ;-)
  3. Custom ASP.NET 2.0 WebParts und Variations vertragen sich leider gar nicht!
    Beispiel: Eine Page erstellen, ein (selbst geschriebenes) WebPart hinzufügen und Publish klicken. Das Ergebnis ist ein leerer Eintrag im Variation Log, wo dann zwar Datum und Uhrzeit, aber weder Success, noch Failure Meldungen stehen.
    Weiterhin passiert dann einfach nichts mehr. Entfernt man das WebPart und klickt erneut auf Publish, funktioniert alles wieder wie es sollte.
    Nach einiger Recherche gibt es dazu offensichtlich 2 Lösungen:
    1. Man erbt von Microsoft.SharePoint.WebPartPages.WebPart anstatt von System.Web.UI.WebControls.WebParts.WebPart, was jedoch laut Microsoft nicht empfohlen ist, oder
    2. Installiert neben den Post-SP1 Hotfixes vom 31. Januar 2008 auch noch das 21. Februar 2008 Hotfix Package, was genau dieses Problem behebt. Somit können dann auch bereits vorhandene ASP.NET WebParts verwendet werden.

Weitere Teile folgen, denn noch ist das Projekt nicht zu Ende... Im nächsten Beitrag geht es dann um das ebenfalls allseits beliebte Content Deployment und warum  ich das ganze nahezu komplett neu Entwickelt habe...

SharePoint Menu Styles: Reihenfolge im CSS File

Heute mussten wir eine sehr fragwürdige Entdeckung machen:
Bei SharePoint/MOSS ist die Reihenfolge der styles im .css file wichtig! Ok genauer gesagt liegt es wahrscheinlich am ASP.NET Menu Control.

Wie kommt man nun zu so einer Erkenntnis?

Dazu fügt man ein neues SharePoint Menu Control zu einer MasterPage hinzu, alternativ auch das Standard ASP.NET Menu Control.
Nun die CSS Klassen für die verschiedenen Level festlegen, und auch die Selected Eigenschaft.

Die Styles liegen dabei in einem extra .css file, was etwa so aussieht:

.NavBarSelected { (...) }

.NavBarLevel1, .NavBarLevel2 { (...) }

Nun werden die MenuItems zwar korrekt dargestellt, aber nur bis man eines selektiert. Das Problem ist hier, dass SharePoint, oder eben das ASP.NET Menu, den HTML code folgendermaßen rendert:

...menuitem class=“NavBarSelected, NavBarLevel1, NavBarLevel2“

Es werden einfach alle styles in die class Eigenschaft gerendert, nur leider in der Reihenfolge in der sie auch im .css file aufgeführt sind.

Vertauscht man im .css file nun einfach die Reihenfolge der class angaben so

.NavBarLevel1, .NavBarLevel2 {} 

.NavBarSelected {} 

dann wird das Menu richtig gerendert und das Ergebnis sieht so aus:

...menuitem class=“NavBarLevel1, NavBarLevel2, NavBarSelected“

Dabei sollte man im Normalfall davon ausgehen können, dass wenn man schon das "selected" class property setzt, die style angabe automatisch ans Ende gesetzt wird und man nicht in einem mehrere 1000 Zeilen großen .css file auf die Reihenfolge achten muss...

MOSS 2007 SP1

Jetzt gibt es also auch das Service Pack 1 für Microsoft Office SharePoint Server 2007.

Vorher muss das SP1 für WSS 3 installiert werden. Downloads hier:

WSSv3 SP1: http://www.microsoft.com/downloads/details.aspx?FamilyId=4191A531-A2E9-45E4-B71E-5B0B17108BD2&displaylang=en

MOSS 2007 SP1: http://www.microsoft.com/downloads/details.aspx?FamilyID=ad59175c-ad6a-4027-8c2f-db25322f791b&DisplayLang=en

Customize Action Menu in MOSS 2007

In einer Document Library gibt es ja ein paar Menüs. Nun möchte man jedoch einige Funktionalitäten nicht jedem User zur Verfügung stellen. In diesem Beispiel möchte ich "Open in Explorer" nicht mehr im Action Menü haben.

Dafür gibt es zwei Vorgehensweisen:

  1. Den Usern das UseRemoteAPIs Recht entziehen wenn möglich
  2. Eine Anpassung des Action Menüs (nicht offiziell supported!) in dem man folgendes macht:

Die Datei "DefaultTemplates.ascx" im Ordner "C:\Program Files\Common Files\Microsoft Shared\web server extensions\12\TEMPLATE\CONTROLTEMPLATES" öffnen, nach der Zeichenfolge ID="ToolbarActionsMenu" suchen. Dort finden sich die entsprechenden Einträge als SharePoint:MenuItemTemplate. Nun kann man z.B. das gesamte Item entfernen, aber auch neue hinzufügen.

SPGridView Sorting Arrow

Setzt man das SharePoint GridView (Microsoft.SharePoint.WebControls.SPGridView) in eigenen Seiten ein, und benutzt man für die Datenquelle die DataSource Eigenschaft

spGridView.DataSource = dataView;

dann zeigt das GridView beim sortieren keine Pfeile neben der sortierten Spalte an.

Dank Reflector stellt man fest, dass das SPGridView leider die Spalten Sortierung nur anzeigt wenn man als Datenquelle "DataSourceID" verwendet... Warum? Ich sehe da keinen Grund. Deshalb hier in etwa die Implementierung wie es das SPGridView auch intern macht:

      public static void SetGridViewSortArrow(SPGridView spGridView, string sortExpression, SortDirection sortDirection)
      {
         // Show arrow on sorted column
         for (int colIndex = 0; colIndex < spGridView.Columns.Count; colIndex++)
         {
            DataControlField field = spGridView.Columns[colIndex];
            if (((field == null) || string.IsNullOrEmpty(field.SortExpression)) 
|| (field.SortExpression.ToLower(CultureInfo.CurrentCulture) != sortExpression.ToLower(CultureInfo.CurrentCulture))) continue; DataControlFieldHeaderCell cell2 = (DataControlFieldHeaderCell)spGridView.HeaderRow.Cells[colIndex]; Image image = new Image(); if (sortDirection == SortDirection.Ascending) image.ImageUrl = "/_layouts/images/sortup.gif"; else image.ImageUrl = "/_layouts/images/sortdown.gif"; image.Style[System.Web.UI.HtmlTextWriterStyle.MarginLeft] = "2px"; cell2.Controls.Add(image); break; } }

Aufgerufen wird das ganze dann im gridView_Sorting Event nach dem sortieren und vor allem nach dem

spGridView.DataBind();

MOSS 2007 API + STC Vorbereitung

Länger gab es nichts Neues, deshalb hier ein kurzes Update.

1. Ein MOSS 2007 Problem:

Ich versuche über folgende Zeilen (aus einer Windows Forms Anwendung) eine Verbindung zum SharePoint aufzubauen:

SPSite site = new SPSite("http://moss2007/sites/test");
SPWeb web = site.OpenWeb();
SPListCollection lists = web.Lists;

Bereits in der ersten Zeile bekomme ich eine Exception (ausgeführt mit einem MOSS Admin Account) geworfen die auch noch relativ unverständlich ist: "FileNotFoundException: The web application at http://moss2007/sites/test could not be found. (...)"

Mit einem globalen Domain Admin Account (der kein eingetragener MOSS Admin ist, aber auf allen System in der Domäne admin permissions hat) läuft das ganze hingegen ohne Probleme. Ich habe schon rausgefunden, dass der Account in der WSS_Admin_WPG Gruppe sein muss und zusätzlich Zugriff auf die SharePoint_Config Datenbank haben muss, dann gehts... aber wieso? Was genau macht dieser API Call, wenn er nicht über das SharePoint User Objekt Modell geht? Falls jemand eine Erklärung oder ein paar Links zur Hand hat, immer her damit.

2. Die STC 2007 ist zwar noch etwas hin, aber meine Präsentation zum Thema AntMe werde ich bereits auf unserem Avanade Team Meeting vorstellen, dementsprechend bin ich dabei diese bereits vorzubereiten. Einige Plugins sind dafür in der Entstehung und die Slides füllen sich auch langsam mit Inhalt...
Die Agenda steht inzwischen übrigens auch schon fest.