Herzlich Willkommen im Communardo Techblog, dem Entwickler-Weblog von Communardo. An dieser Stelle werden wir in loser Folge Ideen, Problemlösungen, Tipps und Problemstellungen rund um die Entwicklung webbasierter Software auf Basis verschiedenster Technologien vorstellen. Wir freuen uns über Rückmeldungen und Kommentare unserer Leser. Viel Spaß beim Lesen und Kommentieren.

01Jul

Heute habe ich versucht, über ein Nutzerinterface (GUI) eine Site Collection (Websitesammlung) anzulegen, welche über eine eigene Url erreichbar ist (Host Named Site Collection). Wie erwartet ging dies nicht ohne jegliche Komplikationen über die Bühne.

Um eine Host Named Site Collection anzulegen, stehen dem Entwickler zwei Varianten zur Verfügung. Die erste und von mir bevorzugte ist die programmatische:

Mit der folgenden Methode (z.B. in einer Konsolenanwendung) kann eine Host Named Site Collection angelegt werden:

void createHostNamedSC(string siteUrl,string siteName,string siteDescription)
{
SPWebApplication webApp = SPWebApplication.Lookup(new Uri(SPContext.Current.Web.Url));
SPSiteCollection sites = webApp.Sites;
SPSite Site = null;
Site = sites.Add(siteUrl, siteName, siteDescription, 1033, “STS#0″, “Domain\\Administrator”, “Owner_Display_Name”, “Owner_Email”, “Domain\\Administrator”, “Secondary_Owner_Display_Name”, “Secondary_Owner_Email”, true);
}

Die zweite Variante wird über stsadm realisiert:
Dazu wird in der Console folgender Aufruf gestartet:


stsadm.exe -o createsite
-url http://hnsc.webapplication.com
-ownerlogin Domain\Administrator
-owneremail Administrator@webapplication.com
-hhurl http://www.webapplication.com

Die Webapplication stellt dabei der Eintrag hinter -hhurl (http://www.webapplication.com) dar.
Unter -url wird die gewünschte URL der neuen Site Collection angegeben.

Hat man sich für eine der beiden Methoden entschieden und diese ausgeführt, müssen noch die Hostheader für die Webapplikation angepasst werden. Das Hinzufügen der Hostheader kann einerseits über die Sharepoint Zentral-Administration erfolgen oder aber über den Internet Information Services (IIS) Manager.
Ich habe mich dabei für die Einstellung innerhalb des IIS entschieden.

Dazu öffnet man den IIS Manager und öffnet über das Kontext-Menü die Einstellungen (Properties) der Webapplikation.

iis1

iis2

  • Anschließend wählt man Advanced
  • Menüpunkt Add wählen
  • Als letzter Schritt muss der Port und die Domain noch eingetragen werden
  • Alles bestätigen und einen IIS Reset durchführen (ggf. host Datei Domain eintragen)

Nach diesen Schritten sollte die eben erstelle Site Collection unter ihrer Domain aufrufbar sein.

Nachdem ich diese Schritte alle erfolgreich ausgeführt hatte, versuchte ich mich nun an meiner neuen Host Named Site Collection anzumelden. Obwohl ich augenscheinlich alle Credentials ordnungsgemäß eingegeben hatte, bekam ich folgende Seite zu sehen:

HTTP 401.1 – Nicht autorisiert: Fehler bei der Anmeldung

iiserror

Nachdem ich schon fast anfangen wollte, mir die Haare auszureißen, fand ich eine Bugbeschreibung von Microsoft unter

Fehler 401.1 beim Aufrufen einer Website

Laut Microsoft tritt dieser Fehler nur auf, wenn die Website integrierte Authentifizierung verwendet und ihr Name der lokalen Loopbackadresse zugeordnet ist. Wenn dann noch Windows Server 2003 mit SP1 installiert ist, schlägt der Fehlerteufel zu. Demnach schlägt die Authentifizierung fehl, wenn der Domänenname oder der benutzerdefinierte Hostheader nicht mit dem lokalen Computernamen übereinstimmen.

Um den Bug zu beheben, müssen folgende Schritte ausgeführt werden.

  • Registry Editor öffnen mit “regedit”
  • Schlüssel HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa suchen
  • auf LSA mit rechter Maustaste drücken ->Neu->DWORD
  • Name DisableLoopbackCheck vergeben und Wert 1 eintragen
  • Computer neu starten

Nach dem Neustart war ich in der Lage, die Site Collection unter ihrer Domain aufzurufen.


01Jul

Sollte man einmal zufällig die Windows Search auf einem Windows Server 2003 x64 installiert haben, wird man von folgender Fehlermeldung beglückt.

Ereignistyp:        Fehler
Ereignisquelle:        Windows Search Service
Ereigniskategorie:        Gatherer
Ereigniskennung:        3083

Beschreibung:

Fehler beim Laden des Protokollhandlers Search.Mapi2Handler.1. Fehlerbeschreibung: Klasse nicht registriert

Um diesen Fehler aus der Ereignisanzeige zu verbannen sind folgende Schritte notwendig.

  1. Man öffnet den Registrierungs-Editor(Start -> Ausführen ->  „Regedit“).
  2. Dann Sichert man die Registry(Rechte Maustaste auf Arbeitsplatz -> Exportieren).
    image001
  3. Nachdem man alles in Sicherheit gebracht hat muss man einen weiteren Export durchführen. Dazu wählt man im “Registrierungsdatei exportieren” Fenster im Exportbereich “Ausgewählte Teilstruktur” aus. Folgender Schlüssel(HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{9E175BAF-F52A-11D8-B9A5-505054503030}) muss dafür exportiert werden.
    image003
  4. Nun öffnet man die soeben erstellte Teilstruktur Sicherung mit einem beliebigen Editor.
  5. Wählt Bearbeiten -> Ersetzen.
  6. Und sucht nach “\Wow6432Node” und ersetzt dies mit einer leeren Zeichenkette.
    image006
  7. Am Ende der Prozedur müsste das Ergebnis so oder so ähnlich aussehen.
    Diese Datei muss man dann einfach nur noch speichern und mit einem Doppelklick(auf die Datei) installieren.
    image008
Technorati Tags: , , , , , ,

28Jun

Um in die Ausführung von Skripten zur Laufzeit einzugreifen gibt es das Feature der Scripting Events. Fast jedes Grails-Skript beinhaltet ein oder mehrere Einstiegspunkte (Hooks), welche man abgreifen und dann im Kontext des gerade laufenden Skriptes eigene Aktionen ausführen kann. Dazu wird einfach im Ordner /scripts die Datei _Events.groovy erstellt und von dort aus lässt sich nun auf einfache Weise überall “einhaken”.

Im letzten Projekt nutzten wir dieses Feature um zusätzliche Properties in der web.xml zu ersetzen und das funktionierte auch wunderbar in der lokalen Windows-Entwicklungsumgebung. Leider aber nicht auf der Build-Maschine, welche unter einem Unix-Betriebssystem lief. Der Fehler war nach langem Probieren ausgemacht – beim Anlegen des Skriptes wurde fälschlicherweise der Name _events.groovy anstatt _Events.groovy vergeben. Anscheinend ist Windows etwas weniger Restriktiv was den Aufruf der Datei anhand des Namens betrifft, ein Umbennen schaffte letztendlich abhilfe.

Technorati Tags: , , , , ,

25Jun

Die letzten Tage habe ich damit verbracht, mich mit den Besonderheiten(oder besser absurden Angewohnheiten) von Sharepoint 2007  zu befassen. Speziell mit dem programmatischen Anlegen von Ordnern und Unterordnern in Sharepoint Dokumenten – Bibliotheken beziehungsweise Listen.

Hier meine gesammelten Erfahrungen über das Anlegen von Ordnern in

  • Bild- und Dokumentenbibliotheken (Picture- and Documentlibrary)
  • Webseiten Bibliotheken (Pages Library)
  • Wiederverwendbare Inhalte (Reusable Content)

Bild- und Dokumentenbibliotheken (Picture- and Documentlibrary)

Angefangen hat das ganze mit dem Versuch, Unterordner in einer Bild- und Dokumentenbibliothek anzulegen. Hier stellte sich Sharepoint gutmütig. Innerhalb dieser Bibliotheken kann über das Menü (Neu /New) ein Ordner angelegt werden, in welchen später auch Inhalte gespeichert werden können.
Soll das Anlegen der Ordner programmatisch (zum Beispiel über ein Feature) erfolgen,  kann folgender Programmcode verwendet werden.

String folderList = "Bilder";
SPList spList = webContext.Lists[folderList];
String folderUrl = spList.RootFolder.ServerRelativeUrl.ToString();
SPFolderCollection foldersColl = web.GetFolder(folderUrl).SubFolders;
SPFolder newFolder = folders.Add("Unterordner");
spList.Update();

Bis hierher könnte man meinen, Sharepoint bietet dem Entwickler mit seiner API alle Möglichkeiten, Listen zu modifizieren und so zum Beispiel Ordner anzulegen.

Webseiten Bibliotheken (Pages Library)

Ich wurde jedoch eines Besseren belehrt, als ich versuchte, innerhalb der Webseiten Bibliothek (Pages Library) einen Unterordner anzulegen. Anders als in einer Bildbibliothek bietet hier das Menü nicht die Möglichkeit, einen Ordner hinzuzufügen.

Pagesfolder

Da das Menü fehlte, dachte ich mir, versuche ich es doch einfach per Programmcode. Hierzu kann der obige Code verwendet werden, indem einfach der String für die Liste auf Pages geändert wird.

String folderList = "Pages";

Pagestest

Das Ergebnis sieht wie oben angezeigt aus. Der Unterordner erscheint unterhalb der Webseiten Bibiliothek. Eigentlich das gewünschte Ergebnis … aber dieses Ergbnis hat einen Haken. Unterhalb dieses Ordners können keine Pages angelegt werden und der Unterordner hat auch keinen Menüpunkt innerhalb der GUI für das Anlegen einer Page.

Nach intensiver Recherche fand ich einen Knowledge Article von Microsoft zu diesem Thema.

http://support.microsoft.com/kb/948614/en-us
Daraus geht klar hervor, dass dieses Verhalten von Microsoft nicht unterstützt wird. Schade eigentlich.

Wiederverwendbare Inhalte (Reusable Content)

Der letzte Schritt sollte das Anlegen eines Unterordners unterhalb der Wiederverwendbaren Inhalte Bibliothek (Reusable Content) darstellen.  Da das Menü die Möglichkeit bietet, bequem einen Unterordner in dieser Liste zu erstellen, dachte ich, der Programmcode von oben sollte sehr gut dafür funktionieren … weit gefehlt. Wieder wurde ich eines Besseren belehrt. Der Code bewirkte in dieser Liste rein gar nix.

Nach einer Phase des Debuggens wurde mir klar, wie Sharepoint hier arbeitet. Anstatt einen SPFolder anzulegen, muss hier ein ein neues SPListItem angelegt werden. Der Trick an der Sache ist, diesem SPListItem den gleichen SPContentType zuzuweisen wie Sharepoint ihn verwendet, um einen Ordner anzulegen.

Wir benötigen den ContentType für Folder. Wird dieser ContentType einen SPListItem zugewiesen, erscheint dieses danach auch in der Wiederverwendbaren Inhalte Liste als Ordner und besitzt alle nötigen Menüpunkte. Der passende SPContentType kann wie folgt ermittelt werden:


SPContentTypeId contentId = new SPContentTypeId("0x0120");
SPContentTypeId listCTID = reuseList.ContentTypes.BestMatch(contentId);
SPContentType cType = reuseList.ContentTypes[listCTID];
List contentTypes = new List();
contentTypes.Add(cType);

Das Ganze habe ich anschließend in eine generische Methode verpackt, welche wie folgt aussieht:

CodeSnippet
Diese Methode legt nun ein SPListItem an, welches den Content Type Folder besitzt und auch entsprechend in der Liste angezeigt wird. Da der Ordner im Verzeichnisbaum jedoch nicht derselbe ist wie in der Liste angezeigt, muss  dieser noch umbenannt werden.  Anschließend kann dieser Ordner genauso verwendet werden, als ob er über das grafische Menü angelegt worden wäre. War doch ganz einfach… – oder?
Zum besseren Kopieren hier das Ganze nochmal als Text:

internal static void SubFolder2ReusableList(SPList reuseList, string reusableFolder)
 {
 reuseList.ContentTypesEnabled = true;
 //get the content type for the folder
 SPContentTypeId contentId = new SPContentTypeId("0x0120");
 SPContentTypeId listCTID = reuseList.ContentTypes.BestMatch(contentId);
 SPContentType cType = reuseList.ContentTypes[listCTID];
 List<SPContentType> contentTypes = new List<SPContentType>();
 contentTypes.Add(cType);

 SPListItem newListItem = reuseList.Items.Add();

 newListItem[SPBuiltInFieldId.ContentTypeId] = cType.Id;
 newListItem[SPBuiltInFieldId.Title] = reusableFolder + "_tmp";
 newListItem.Update();
 reuseList.Update();

 //get the parent folder of the SPListItem which is resident in the left tree
 //its not the same folder as listed in die detail list on the right
 using (SPWeb webContext = reuseList.ParentWeb)
 {
 SPFolder reuseFolder = webContext.GetFolder(newListItem.Url);
 try
 {
 //try to rename the folder on the left
 reuseFolder.MoveTo(reuseList.RootFolder.ServerRelativeUrl + "/" + reusableFolder);
 }
 catch (SPException)
 {
 //if the folder already exist delete it
 SPFolder toDeleteFolder = webContext.GetFolder(reuseList.RootFolder.ServerRelativeUrl + "/" + reusableFolder);
 toDeleteFolder.Delete();
 reuseFolder.MoveTo(reuseList.RootFolder.ServerRelativeUrl + "/" + reusableFolder);

 }
 //rename the _tmp SPListitem to his final name
 newListItem[SPBuiltInFieldId.Title] = reusableFolder;
 newListItem.Update();
 }
 }

18Jun

Heute fand das 8. Treffen der .Net Usergroup Dresden in der T-Systems MMS statt. 2 Themen standen auf dem Programm:

Obgleich sich der Blick auf Visual Studio 2010 genau genommen mehr als Blick auf PowerPoint entpuppte, gab es doch einiges Wissenswerte über nette neue Features zu erfahren, wie z.B. das Historical Debugging oder die Möglichkeit, vorhersagbare ClientIDs festzulegen in ASP.Net 4.0 – eine “Kleinigkeit”, auf die wohl schon Generationen von ASP.Net-Entwicklern gewartet haben. Interessant fand ich auch den Hinweis auf die IDE Extensions in der Visual Studio Gallery und dort speziell auf das in VS2010 integrierbare Demo Dashboard, ein CodePlex-Projekt.

Wer (in Form von PowerPoint’s und Demos) mehr über die Neuerungen erfahren möchte, dem sei das Visual Studio 2010 and .NET Framework 4 Training Kit empfohlen, ebenfalls ein Tipp von Robert. Wer selbst vorab schon mal etwas mit dem neuen Visual Studio herumspielen möchte, kann sich hier die Beta herunterladen.

Beim Vortrag über Lambda Expressions & LINQ strich Oliver erstmal das “LINQ” weg (das wird für einen späteren Vortrag reserviert) und setzte stattdessen “Extension Methods & Generic Delegates” ein – und genau darum drehte sich der Vortrag dann auch. Es gab jede Menge Codebeispiele für Lambda Expressions, ganz speziell ging es um die Fünf Freunde: Action, Predicate, Comparison, Converter und Lambda Ausdrücke.

Eine kleine Diskussion um die Vor- und Nachteile von LINQ2SQL und Entity Framework und die Frage “Welches von beiden verwenden? – Oder doch lieber NHibernate – Oder einen ganz anderen oder gar keinen O/R-Mapper?” rundete den gelungenen Abend ab.

Die Folien der Vorträge gibt es sicherlich wie immer in Kürze hier zum Download.


16Jun

Gestern Abend fand das erste Treffen der Agile Saxony in Dresden statt. Es fanden sich gleich zum ersten Termin mehr als 40 Personen ein, was die Erwartungen des Organistors Jens Korte deutlich übertraf.

Simon Roberts (Scrum Coach und Certified Scrum Trainer) führte in das Thema mit den Vortag “What the hell is Scrum?” ein und begann mit folgenden Cartoon.

Im im Agilen Manifest u. a. von Ken Schwaber und Jeff Sutherland wurde Scrum wie folgt formuliert:

  1. Individuen und Interaktionen gelten mehr als Prozesse und Tools.
  2. Funktionierende Programme gelten mehr als ausführliche Dokumentation.
  3. Die stetige Zusammenarbeit mit dem Kunden steht über Verträgen.
  4. Der Mut und die Offenheit für Änderungen steht über dem Befolgen eines festgelegten Plans.

Ziel ist es ist mit einer inkrementellen Vorgehensweise, der Organisation von Entwicklungsabschnitten und Meetings in vordefinierten Zeitabschnitten (Time-Boxes) ein funktionierendes Produkt zu entwicklen.

Links zum Thema:

http://www.scrumalliance.org/

http://scrum-master.de/

http://www.agilesaxony.org/


15Jun

Auch dieses Jahr startet das Team der Communarden wieder beim schon legendären Drachenbootfestival des Dresdner Elbhangfestes – wie es sich für eine IT-Firma gehört, im Pokal “Bits & Bytes”.

Getreu unserem Motto “Wir sind Gewinner – selbst wenn wir nicht die Schnellsten sein sollten!” waren wir schon zum gestrigen Training mit großem Einsatz und sehr viel Spaß bei der Sache. Nun bleiben noch ein paar Tage Zeit, um den Muskelkater zu pflegen, bevor es dann am 27. Juni ums Ganze (nämlich eine ganze Eistorte) geht und weithin unser Schlachtruf erschallt: “Communarden – paddeln, paddeln, paddeln!”

Communarden - paddeln, paddeln, paddeln!

Technorati Tags: ,

12Jun

Am Donnerstag den 11.06.2009 fand unser 4. Treffen der Sharepoint Usergroup Dresden in den Räumen der T-Systems MMS statt.

Für unser gestriges Treffen konnten wir Steffen Krause (Technical Evangelist, Microsoft Deutschland) gewinnen. Sein Vortrag stand unter dem Thema ” SQL Server Grundlagen für SharePoint Administratoren”. In einem interessanten Vortrag erläuterte er die Unterschiede der verschiedenen SQL Server Editionen, wie man seine Hardware für den Einsatz mit SQL Server plant und zeigte verschiedene Tools für den Einsatz mit Datenbanken.

Im zweiten Teil seines Vortrages erläuterte er die Besonderheiten von SQL Server für den Umgang mit Sharepoint und welche Grenzen durch das System gesetzt sind. Ein anschließender Überblick über Performanceüberwachung innerhalb von Sharepoint sowie der dazugehörigen Datenbankwartung rundete den Vortrag ab.

Die uns näher gebrachten Best Practices in Umgang mit Sharepoint sowie SQL allgmein festigte er mit einer anschließenden Demo des SQL Servers und seiner verschiedenen Wiederherstellungsmethoden.

Hier mal die versammelte Runde:

Sharepoint Usergroup Dresden

Interessante Links:

Defragmentieren von Windows SharePoint Services 3.0- und SharePoint Server 2007-Datenbanken

Folien zu dem Vortrag:


02Jun

Steht man vor der Aufgabe einen Sharepoint optisch anpassen zu müssen, ist oft die Masterpage anzupassen. Wird der Sharepoint lediglich als interner Portalserver verwendet, welcher nur von einer bestimmten Zielgruppe genutzt wird, reicht dazu meist das Anlegen einer eigenen Masterpage aus.

In einem aktuellen Projekt stand ich jedoch vor dem Problem, Gästen sowie Administratoren jeweils eine eigene Masterpage zur Verfügung zu stellen. Sharepoint selber bietet keine Möglichkeit zu unterscheiden ob gerade ein Gast oder ein Nutzer auf eine Seite zugreift und eine entsprechende Masterpage auszuliefern.

Auf der Suche nach einer Lösung im Internet wurde ich relativ schnell fündig, jedoch funktionierten die angebotenen Lösungen nicht für Publishing Pages oder Template Redirection Pages.

Um die Masterpage dynamisch für Publishing Pages zu ändern, verwendet man die nachfolgende Eigenschaft:

SPContext.Current.Web.CustomMasterUrl

Die nächste Eigenschaft kann nur verwendet werden, wenn es sich nicht um Publishing Pages handelt oder man die “application.master” verändern will.

page.MasterPageFile = "/_catalogs/masterpage/default.master"

Auf Basis dieser Informationen kann nun ein Skript geschrieben werden, welches abhängig davon ob ein Nutzer angemeldet ist oder nicht, die Masterpage setzen kann. Als Ergänzung dazu kann innerhalb der “page_PreInit” die application.master auf eine eigene Masterpage eingestellt werden.

Alles was man dazu tun muss, ist, sich ein neues dll Projekt anzulegen und die fertig kompilierte dll in dem Global Assembly Cache abzulegen. Dabei ist es wichtig die dll zu signieren.

using System;
using System.Web;
using System.Web.UI;
using System.IO;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint;

public class MasterPageSwitch : IHttpModule
{
        public void Init(HttpApplication context)
        {
            context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
        }

     void context_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            Page page = HttpContext.Current.CurrentHandler as Page;

            if (page is PublishingLayoutPage || page is TemplateRedirectionPage)
            {
                HttpApplication currentApp = sender as HttpApplication;
                string parameter = "";
                if (HttpContext.Current.User.Identity.IsAuthenticated)
                {
                    parameter = "/_catalogs/masterpage/adminMaster.master";
                }
                else
                {
                    parameter = "/_catalogs/masterpage/guestMaster.master";
                }
                if (parameter != null)
                {
                    SPContext.Current.Web.CustomMasterUrl = parameter;
                }
            }
            else
            {
                if (page != null)
                    page.PreInit += new EventHandler(page_PreInit);
            }
        }

         void page_PreInit(object sender, EventArgs e)
        {
            Page page = sender as Page;
            if (page != null)
            {
                if (page.MasterPageFile != null)
                {
                    if (page.MasterPageFile.Contains("application.master"))
                    {
                      //auskommentiert da wir die Änderung der application.master nicht benötigen
                      //page.MasterPageFile = "/_catalogs/masterpage/default.master";
                    }
                }
            }
        }

        public void Dispose()
        {
        }
}

Als letzten Schritt fügt man innerhalb der web.config unter der Sektion folgenden Eintrag hinzu :

<add name="MasterPageSwitch" type="MasterPageSwitch, MasterPageSwitch, Version=1.0.0.0, Culture=neutral, PublicKeyToken=................"/>

27Mai

Der iX-Day rund um SharePoint findet am 9. Juli 2009 in Stuttgart statt. Er richtet sich in zwei parallelen Tracks mit durchaus vielversprechenden Vorträgen an unterschiedliche Zielgruppen:

  • SharePoint für Administratoren:
    Installation und Administration von SharePoint sowie die Einrichtung von Windows SharePoint Services
  • SharePoint für Software-Entwickler:
    Programmierung von SharePoint mit Visual Studio .NET und Visual Studio Team Systems sowie Design von Sharepoint

Communardo wird mit einem Vortrag zum Thema Silverlight und SharePoint vertreten sein, welcher einen bei Communardo auf der Basis von AJAX und Silverlight entwickelten Prozess-Editor-Webpart vorstellt. In der Präsentation ist zu erfahren, wie die Vorteile von Silverlight (Vektorgrafiken, Skalierbarkeit, Drag & Drop) mit denen von SharePoint (Versionierung, Berechtigungen, Veröffentlichung) kombiniert werden können – und warum Entwickler manchmal gefährlich leben… ;-)

Wir freuen uns, dass wir uns aktiv an der Konferenz beteiligen und diese hoffentlich mit einem spannenden Vortrag bereichern können!

Technorati Tags: , ,