Dienstag, 17. Januar 2012

SharePoint 2010 + InfoPath 2010 + Excel Services + REST API

Bei der Realisierung eines Urlaubsantragssystems auf Basis von SharePoint 2010 kann es mitunter zu einigen Problemen kommen.

Einige Fragen, die vor der Realisierung beantwortet werden müssen sind folgende:

     - Wo wird der Stand der Urlaubstage verwaltet
     - Wie wird die Berechnung der verbrauchten Urlaubstage durchgeführt
     - Wie sieht der Prozess für den Urlaubsantrag aus
     - Tragen die Mitarbeiter die genommenen Urlaubstage händisch ein, oder soll dies automatisch 
        berechnet werden ?

All diese Fragen führten dazu, dass für das Formulardesign InfoPath 2010 verwendet wurde, um eine schöne Oberfläche für das Urlaubsantrags-Formular zu bekommen.

Das Formular hatte einige Steuerelemente wie z.B. People picker, Textboxen und Datumsfelder für das Datum des 1. und des letzten Urlaubstages.

Und genau bei diesem Punkt fängt das Problem der Urlaubstagsberechnung an.
InfoPath kann zwar die Differenz von 2 Datumswerten berechnen, was allerdings fehlt ist die Berechnung unter Berücksichtigung der Wochenenden und Feiertage.

Hier kommen die Excel Services ins Spiel. Excel besitzt nämlich die Funktion NETWORKDAYS, die die Arbeitstage zwischen zwei Datumswerten berechnet.

Wie bekommt man nun die Datumswerte des InfoPath Formulars in den Excel Sheet, der dann dort die Berechnung der Arbeitstage ausführt und diese dann an das Formular retourniert ?

Die Antwort lautet REST API. Die REST API macht genau das möglich. Im weiteren Verlauf dieses Posts werde ich auf die Umsetzung dieser Funktionalität unter Zuhilfenahme der verschiedenen Technologien im Detail eingehen.

Also beginnen wir zunächst mit dem Excel Sheet.
Hierfür erstellen wir einen neuen Excel Sheet und verwenden die ersten 2 Zellen in der Spalte A für unsere Inputs.
Ich habe die Felder in diesem Fall "StartDate" und "EndDate" genannt.
In der 1. Zelle in Spalte B soll dann unser berechneter Wert stehen.
Dazu fügen wir die Formel "=NETWORKDAYS(StartDate; EndDate) in diese Spalte.
Ein Test zeigt, dass die Formel funktioniert:






Jetzt kann der Excel Sheet in eine SharePoint Library via "Save & Send" gepublished werden. Wichtig hierbei ist, dass die beiden Felder StartDate und EndDate bei den Parametern angehakt werden, um diese Felder über die REST url dann verfügbar machen zu können.


 


Ein wichtiger Faktor ist das Datumsformat. In meinem Fall war das Datumsformat English (UK).
Wichtig ist, dass dieses Format auf jeden Fall korrekt übergeben wird, sonst wird ein falscher Wert vom REST Service zurückgeliefert.

Um zu testen, ob REST funktioniert, wird dann folgende Url im Internet Explorer eingegeben:

http://<server>/<site>/_vti_bin/ExcelRest.aspx/<library>/<ExcelSheet>.xlsx/Model/Ranges('DateDiff')?Ranges('StartDate')=2012-01-13&Ranges('EndDate')=2012-01-17

DateDiff ist in unserem Fall die Zelle, die die Formel für die Berechnung enthält.
Haben wir alles richtig, gemacht, erscheint folgende Ausgabe im Internet Explorer:


 

Da das Format des Rückgabewertes zum jetzigen Zeitpunkt noch HTML ist und wir in InfoPath XML benötigen, fügen wir noch den Ausdruck $format=atom zu unserer Url hinzu, sodaß die Url dann folgendermaßen aussieht:

http://<server>/<site>/_vti_bin/ExcelRest.aspx/<library>/<ExcelSheet>.xlsx/Model/Ranges('DateDiff')?$format=atom&Ranges('StartDate')=2012-01-13&Ranges('EndDate')=2012-01-17

Da wie bereits erwähnt das Datumsformat sehr wichtig ist, stellen wir im InfoPath Formular gleich das richtige Datumsformat in dem Date Picker ein. Dies geschieht über die Properties des DatePickers und war in meinem Fall English (UK).


 


Nachdem das Design des InfoPath Formulars abgeschlossen ist, binden wir nun das REST Service in unser Formular mit ein.
Dazu fügen wir zuerst das REST Service als Datenquelle hinzu.













In das Url Feld fügen wir unsere Url von vorher (inklusive dem $format=atom Parameter) ein und bei "Automatic retreive" entfernen wir den Haken in der Checkbox.

Jetzt müssen die Datumswerte noch von den Feldwerten ersetzt werden.
Hierzu fügen wir für jedes Datumsfeld eine Regel hinzu.

Zunächst für das Startdatum:



 


Als Action ändern wir die REST Url:


Die Funktion substring-before(<feldname>, "T00:00:00") muss durchgeführt werden, da InfoPath diesen Zeitwert an den Datumswert anhängt und wir kein vernünftiges Ergebnis bekommen, wenn wir diesen Zeitstempel nicht entfernen.

Diese Rule wird auch für das EndDatum angelegt und die REST Url geändert:


 


Im nächsten Schritt wird für das EndDatum Feld noch eine Regel erstellt, die die neu erstellte REST Url an das REST Service sendet:


 


Als nächster Schritt wird das Ergebnis der Query - also die Anzahl der Urlaubstage - in einem Feld gespeichert:


Als Wert wird aus der Secondary Data Source das REST Service ausgewählt:


 


Damit ist die Funktionalität für die Berechnung der Urlaubstage implementiert.
Zusätzlich verwalte ich den aktuellen Urlaubsstand der Mitarbeiter noch in einer Liste und wenn der Urlaubsantrag approved wird, wird der Stand der Urlaubstage entsprechend korrigiert.

Mittwoch, 11. Januar 2012

SharePoint 2010: Unexpected Error bei User Profile Service Application

Bei einigen SharePoint Installationen kann es zu einem merkwürdigen Verhalten der User Profile Service Application kommen.

Folgendes Verhalten kann stellenweise auftreten:

In der Central Administration wird die User Profile Service Application unter Application Management -> Manage Service Applications ausgewählt.

Nach Klick auf die User Profile Service Application und einer kurzen Ladezeit erscheint die Meldung, dass ein unerwarteter Fehler aufgetreten ist.

Da diese Aussage nicht sonderlich hilfreich ist, findet sich der Fehler erst nach dem Durchsuchen der ULS Logs.

Filtert man hier auf UserProfileServiceApplication, trifft man auf folgende Fehlermeldung:

System.MissingMethodException: Method not found: 'System.String
Microsoft.Office.Server.UserProfiles.SynchronizationRunStatus.get_ConnectionName()'.
at Microsoft.SharePoint.Portal.WebControls.UserProfileServiceImportStatisticsWebPart.
RenderSectionContents(HtmlTextWriter writer)
at
Microsoft.SharePoint.Portal.WebControls.UserProfileServiceImportStatisticsWebPart.RenderWebPart(HtmlTextWriter writer)
at Microsoft.SharePoint.WebPartPages.WebPart.Render(HtmlTextWriter writer)


Es gibt mehrere Möglichkeiten, dieses Problem zu lösen, allerdings sei hier erwähnt, dass nicht jede Möglichkeit zwingend zur Lösung führt, sondern einer dieser Ansätze das Problem behebt.

Die einfachste und schnellste Lösungsmethode ist die Durchführung eines IISReset über die Eingabeaufforderung.

Sollte dies nicht helfen, besteht die Möglichkeit, dass bei der Provisionierung der User Profile Service Application und der dazugehörigen Datenbanken kein Standardschema für den Farm Administrator zugewiesen wurde.

Dies kann durch ein SQL Statement auf die betreffenden Datenbanken

     - Profile Database
     - Synchronization Database
     - Social Tagging Database

nachträglich durchgeführt werden.

WICHTIG: Da ein Zugriff auf die Content DB's automatisch den Produktsupport seitens Microsoft aufhebt, sei an dieser Stelle gesagt, dass dies KEIN Eingriff in die Datenbank ist.

Das SQL Statement, um das Schema an den Benutzer zuzuweisen lautet wie folgt:

ALTER USER [Domain\Username] WITH DEFAULT_SCHEMA=dbo;

Allerdings kann es passieren, dass auch dieser Vorgang das Problem nicht behebt.

Die letzte Lösungsmöglichkeit, bevor man einen Microsoft Support Call eröffnet, ist die Installation des December 2011 Cumulative Update Paketes für SharePoint Server 2010.

Diese Installation hat in meinem Fall das Problem behoben und auch in diversen Foren und Newsgroups war zu lesen, dass die Installation des Cumulative Update Paketes dieses Problem löst.

Zu finden ist das Paket unter folgender Url: http://support.microsoft.com/kb/2597014

Nach der Installation des Paketes muss der Farm Configuration Wizard erneut ausgeführt werden, da alle Farm Komponenten dann upgedated werden.

Nach Abschluss des Wizard kann man dann versuchen, noch einmal die User Profile Service Application in der Central Administration zu öffnen.

In meinem Fall funktionierte dies dann ohne weitere Probleme.

Falls jemand noch andere Möglichkeiten zur Behebung des Fehlers findet, freue ich mich darüber, wenn er / sie mir diese Lösung mitteilt. Gerne veröffentliche ich sie auch hier.

Dienstag, 10. Januar 2012

Active Directory Benutzer mit PowerShell erstellen

Bei der Einrichtung von Testumgebungen ist es hilfreich, wenn man einige Testuser zur Verfügung hat, mit denen man unterschiedliche Dinge demonstrieren bzw. ausprobieren kann.
Da ich persönlich die Erstellung dieser Benutzer per Hand etwas umständlich finde, habe ich ein PowerShell Skript gebastelt, mit denen sich die Benutzer automatisiert anlegen lassen.

Die Benutzerinformationen stammen aus einer .csv Datei, die nur einmal befüllt werden muss und immer wieder eingesetzt werden kann.

Die .csv Datei enthält folgende Spalten:

     - UserName
     - GivenName
     - Surname
     - samAccountName
     - DisplayName
     - Password


Der  PowerShell Code für den Import der Benutzer gestaltet sich wie folgt:


$users = import-csv "C:\ADUsers.csv"
$container = [ADSI] "LDAP://cn=Users,dc=mydomain,dc=local"
$users | foreach {
     $UserName = $_.UserName
  $GivenName = $_.GivenName
  $Surname = $_.Surname
  $SamAccountName = $_.samAccountName
  $DisplayName = $_.DisplayName
  $UserPassword = $_.Password
  $newUser = $container.Create("User", "cn=" + $UserName)
  $newUser.Put("givenName", $GivenName)
  $newUser.Put("Surname", $Surname) 
  $newUser.Put("sAMAccountName", $SamAccountName) 
  $newUser.Put("DisplayName", $DisplayName)
  $newUser.SetInfo()
  $newUser.psbase.InvokeSet('AccountDisabled', $false)
  $newUser.SetInfo()
  $newUser.SetPassword($UserPassword)
}


Wichtig ist hier die Reihenfolge des setzen der properties, danach muss ein SetInfo() erfolgen.
Erst danach kann der Account enabled werden und diese Reihenfolge muss eingehalten werden, damit der Account auch wirklich enabled wird.

In oben angeführten Beispiel befinden sich die Passwörter innerhalb der .csv-Datei.
Wer dies nicht möchte, oder das gleiche Passwort für alle Benutzer verwenden möchte, kann
die Zuweisung dieser Property weg lassen und anstelle der Passwortvariable den String bei der SetPassword(<password string>) Anweisung übergeben.

Dieses Skript hat sich für mich als sehr nützlich erwiesen und findet oft Anwendung.

Für etwaige Erweiterungsideen bin ich jederzeit offen und Anregungen sind herzlich Willkommen.
    

Bug SharePoint 2010 Installation All-In One Virtual Machine

Für Demonstrationszwecke bei Kunden und als Spielwiese ist eine virtualisierte SharePoint 2010 Installation eine gute Lösung.

Allerdings gibt es einige Dinge, die hierbei zu berücksichtigen sind.

Abgesehen davon, dass genug Arbeitsspeicher zur Verfügung stehen sollte, um flüssig arbeiten zu können, sollte man im Hinterkopf behalten, dass sich in diesem Fall dann alle Funktionen auf einer virtuellen Maschine zusammengefasst befinden.

Das heißt, folgende Dinge, befinden sich in der VM:
    
     - Betriebssystem (Windows Server 2008 R2 64 Bit)
     - Domain Controller
     - Application Server
     - Mail Server
     - Web Server
     - SharePoint Server 2010
     - SQL Server 2008 R2

Zusätzlich zu diesen Dingen, werden auch die SharePoint Services (z.B. Excel Services, etc.) und auch der User Profile Import zur Synchronisation zwischen AD und MySite auf dieser Maschine durchgeführt.

Speziell, wenn diese Maschine als Domain Controller fungiert gibt es bei der Installation von SharePoint als non-standalone Installation Probleme.

Wenn die SharePoint Setup Routine ausgeführt wird, hat man normalerweise nach der Eingabe des Product Keys die Möglichkeit, auszuwählen, ob die Installation als Standalone (inklusive SQL 2008 Express Edition für die Content Datenbank) oder als Server Farm durchgeführt werden soll.

Befindet sich der Domain Controller nun auf der selben Maschine, bekommt man diese Auswahlmöglichkeit NICHT, sondern es wird auomatisch eine Standalone-Installation durchgeführt, was eindeutig als Bug zu werten ist, auch wenn die Installation sowohl von SQL Server 2008 R2, als auch von SharePoint 2010 auf einem Domain Controller nicht empfohlen wird.

Da es allerdings oft gewollt ist, seine Umgebung auf diese Art und weise aufzubauen (weil z.B. ein normaler SQL Server verwendet werden soll, oder möglicherweise eine weitere Maschine zu einem späteren Zeitpunkt die Farm joinen soll), gibt es eine Möglichkeit, diesen Bug zu umgehen:

Die Lösung ist eine Silent Installation über die Eingabeaufforderung und die Konfiguration in einer Config-Datei zu hinterlegen.

Das Kommando für die Installation lautet wie folgt:

setup.exe /config c:\<location of the config.xml>

Die Konfigurationsdatei, die man an einem beliebigen Ort ablegen kann, gestaltet sich wie folgt:


<Configuration>
    <Package Id="sts">
        <Setting Id="LAUNCHEDFROMSETUPSTS" Value="Yes"/>
        <Setting Id="REBOOT" Value="ReallySuppress"/>
        <Setting Id="SETUPTYPE" Value="CLEAN_INSTALL"/>
    </Package>

    <Package Id="spswfe">
        <Setting Id="SETUPCALLED" Value="1"/>
        <Setting Id="REBOOT" Value="ReallySuppress"/>
        <Setting Id="OFFICESERVERPREMIUM" Value="1" />
    </Package>

    <Display Level="none" CompletionNotice="Yes" />
    <PIDKEY Value="{MSDN KEY FOR EDITION OF SHAREPOINT 2010}" />
    <Setting Id="SERVERROLE" Value="APPLICATION"/>
    <Setting Id="USINGUIINSTALLMODE" Value="0"/>
</Configuration>

Die Installationskonfiguration gibt an, dass es sich um mehrere Packages handelt, die installiert werden sollen.

Eine detaillierte Referenz zu den Möglichkeiten der Installationskonfigurationen befindet sich im Microsoft Technet, erreichbar unter http://technet.microsoft.com/en-us/library/cc261668.aspx

Am wichtigsten sind in diesem Fall folgende Zeilen:

<Setting Id="SERVERROLE"Value="APPLICATION"/>
<Setting Id="USINGUIINSTALLMODE" Value="0"/>

Die 1. Zeile sagt aus, dass der Server als Application Server installiert werden soll, sprich eine neue Serverfarm wird aufgebaut.
Die 2. Zeile sagt aus, dass kein GUI für die Installation verwendet werden soll.

Überprüfen lässt sich der Abschluß der Installation entweder durch folgende Zeile

<Display Level="none" CompletionNotice="Yes" />

In diesem Fall bekommt man nach Abschluss der Installation die Benachrichtigung, dass der Configuration Wizard noch ausgeführt werden soll.

Eine weitere Möglichkeit ist es, den Task Manager zu starten und so lange abzuwarten, bis kein msiexec Prozess mehr ausgeführt wird, wobei ich die CompletionNotice bevorzuge.

Danach wird dann noch der Configuration Wizard ausgeführt und die Installation der Farm ist erfolgreich abgeschlossen.