Nintex Site Workflows in Office 365 starten

Nintex Workflow für Office 365 ist ein wenig anders als man dass vielleicht aus den früheren (On-Premise) Versionen gewohnt ist.

So gibt es z.B. keinen Menu-Eintrag in den Site-Actions oder auch keine Einstellungen in den Site-Settings.

Um einen neune Listen-Workflow zu erstellen kann man den Workflow-Designer direkt aus dem Ribbon starten (anstatt über das Workflow-Menu des Ribbons).

image

Aber wie erstellt man einen Site-Workflow? Indem in die Übersicht des Websiteinhalts wechselt und die Nintex-App startet. Hier kann man neue Site-Workflows erstellen

image

image

Workflow-Status in Office 365

Die Workflow-Veteranen kenne es: wenn ich in SharePoint einen neuen Workflow veröffentliche, dann wird in der Liste eine neue Spalte erstellt, die den Status des Workflows wiederspiegelt.

In der Regel sind das: In Bearbeitung, Abgeschlossen, Fehler und Abgebrochen.

image

Über entsprechende Actions im Workflow, kann man hier auch einen anderen Status rein schreiben, z.B. ob ein Inhalt genehmigt wurde oder in welchem Bearbeitungsschritt sich ein Workflow befindet.

Mit SharePoint 2013 hat Microsoft neben der traditionellen Workflow-Engine aus dem .Net Framework auch die Windows Azure Workflows (WAW) mit eingebunden. Diese sind zunächst einmal komplett unabhängig von SharePoint.

Wenn über die WAW ein Workflow veröffentlicht wird, dann wird für diesen Workflow ebenfalls eine neue Spalte angelegt um den Status anzuzeigen, aber der Workflow schreibt im Gegensatz zu den klassischen Workflows wird beim Starten einer neuer Workflow Instanz keinen initialen Status. Das bedeutet, dass die Spalte zunächst leer bleibt.

image

image

image

Das ist deshalb interessant, weil die Spalte eine Link auf die laufende Workflow-Instanz beinhaltet. Mit einem Klick auf den Link bekommt also direkt alle Aufgaben des Workflows sowie den Workflow-Verlauf angezeigt. Ist die Spalte leer, gibt es als Konsequenz auch keinen Link und somit kommt man nur über das Workflow-Menu des jeweiligen Elements an den Workflowverlauf.

Als Lösung sollte man also als ersten Schritt in einem Workflow immer einen Status setzen – damit sichergestellt ist, dass es den Link in der Spalte auch gibt. Wenn man mit dem SharePoint-Designer einen Workflow erstellt, dann wird automatisch als Status der Name der jeweiligen Workflow-Stufe eingefügt, bei Nintex muss man das manuell mit einer entsprechenden Action machen.

image

image

Zu beachten ist außerdem, dass am Ende des Workflows der Status ggf. noch auf “Abgeschlossen” oder so gesetzt werden sollte. Das gilt sowohl für Nintex als auch für Designer Workflows, sonst bleibt in der Spalte der zuletzt gesetzte Status stehen, der ggf. nicht den tatsächlichen Status des Workflows wiederspiegelt.

image

Gulp: Wie Coffeescript zu JavaScript wird

Nachdem ja nun einfache Dinge mit gulp automatisiert werden können, kann man sich ja so langsam mal an weitere Aufgaben begeben.

Coffeescript kann bei der Arbeit mit JavaScript ja schon mal ganz hilfreich sein, nimmt es einem doch manch lästige Zeremonie von JavaScript ab und fügt gleich noch Best-Practices hinzu. Wenn man nicht immer diese .coffee-Datei nach JavaScript übersetzen (compilieren) müsste.

Mit gulp kann man das sehr gut automatisieren.

npm install gulp-coffee --save-dev

sorgt dafür, dass ich das notwendige gulp-coffee Package habe. Nun also noch eine entsprechen Task im gulpfile.js einfügen.

gulp.task('coffee', function () {
    return gulp.src('app/*.coffee')
        .pipe(coffee())
        .pipe(gulp.dest('app'));
})

In diesem Fall werden also alle von Coffee-Script erzeugten Dateien ebenfalls in dem Verzeichnis app gespeichert.

Insgesamt sieht das Build Script nun so aus:

var gulp = require('gulp'),
    coffee = require('gulp-coffee'),
    uglify = require('gulp-uglify');

gulp.task('html', function () {
    return gulp.src('app/*.html')
        .pipe(gulp.dest('dist'));
})

gulp.task('js', ['coffee'], function () {
    return gulp.src('app/*.js')
        .pipe(uglify())
        .pipe(gulp.dest('dist'));
})

gulp.task('coffee', function () {
    return gulp.src('app/*.coffee')
        .pipe(coffee())
        .pipe(gulp.dest('app'));
})

gulp.task('default', ['html', 'js'], function () {
})

Die Task „js“ ist dabei Abhängig von der Coffee-Task. In der Konsole sieht die Ausführung dann so aus:

D:\projects\html_app>gulp
[14:43:24] Using gulpfile D:\projects\html_app\gulpfile.js
[14:43:24] Starting 'html'...
[14:43:24] Starting 'coffee'...
[14:43:24] Finished 'coffee' after 34 ms
[14:43:24] Starting 'js'...
[14:43:24] Finished 'html' after 47 ms
[14:43:24] Finished 'js' after 43 ms
[14:43:24] Starting 'default'...
[14:43:24] Finished 'default' after 13 μs

Gerade beim Umgang mit Coffeescript ist das Debugging des JavaScript Codes z.B. in der Konsole der Browsers etwas umständlich, denn hier wird ja nicht der eigentliche Coffeescript Code debuged, sondern der von Coffeescript erzeugte JavaScript Code. Moderne Browser können aber mit Hilfe von sogenannte SourceMaps auch den ursprünglichen Quellcode anzeigen und debuggen – hier also Coffeescript. Und auch dabei kann gulp helfen.

Dazu erweitern wir die Task coffee entsprechend.

gulp.task('coffee', function () {
    return gulp.src('app/*.coffee')
        .pipe(sourceMaps.init())
        .pipe(coffee())
        .pipe(sourceMaps.write('../dist'))
        .pipe(gulp.dest('app'));
})

Nun wird für jede Coffeescript-Datei eine entsprechende SourceMap-Datei in dem Verzeichnis dist erstellt.

Gulp: JavaScript-Buildsystem

Nachdem ich ja im letzten Post beschrieben habe, wie man mit Hilfe von node und bower sich JavaScript Bibliotheken für seine Anwendung einbinden kann, will ich nun noch etwas mehr zeigen, was einem Node so zu bieten hat.

Typische Aufgabe während der Entwicklung ist ja, dass die HTML und JavaScript-Dateien vom lokalen Rechner auf einen Web-Server kopiert werden müssen. Dazu müssen zunächst alle Dateien, die auf den Server kopiert werden müssen identifiziert und dann kopiert werden.

In reinen .Net Projekten würde ich für das Deployment vielleicht zu MSBuild greifen – das hilft mir aber in meinem Fall nicht so wirklich weiter.

Mit gulp gibt es ein Buildsystem als Node-Module in JavaScript.

npm install gulp -g

installiert gulp und stellt es global zur Verfügung. Nun kann ich in meinem Projekt ein gulpfile.js erstellen.

Ein einfaches Buildfile kann z.B. so aussehen:

var gulp = require('gulp');
gulp.task('copy', function(){
    return gulp.src('app/*.html')
        .pipe(gulp.dest('dist/'));
})

Dabei werden alle *.html Dateien aus dem Verzeichnis app in das Verzeichnis dist kopiert. Existiert das Verzeichnis dist noch nicht, wird es zuvor erstellt. Dieses Beispiel ist natürlich sehr einfach. Mittels gulp copy kann nun die neue copy-Task von einer Konsole ausgeführt werden:

D:\projects\html_app>gulp copy
[14:25:01] Using gulpfile D:\projects\html_app\gulpfile.js
[14:25:01] Starting 'copy'...
[14:25:01] Finished 'copy' after 17 ms

Ein etwas fortgeschritteneres Buildfile könnte wie folgt aussehen:

var gulp = require('gulp'),
    uglify = require('gulp-uglify');

gulp.task('html', function () {
    return gulp.src('app/*.html')
        .pipe(gulp.dest('dist'));
})

gulp.task('js', function () {
    return gulp.src('app/*.js')
        .pipe(uglify())
        .pipe(gulp.dest('dist'));
})

gulp.task('default', ['html', 'js'], function () {
})

Die Task default wird dabei automatisch aufgerufen, wenn man gulp von der Kommandozeile startet. Als zweiter Parameter der Task werden die abhängigen Tasks angegeben. Diese werden also automatisch zuvor ausgeführt.

D:\projects\html_app>gulp
[14:26:15] Using gulpfile D:\projects\html_app\gulpfile.js
[14:26:15] Starting 'html'...
[14:26:15] Starting 'js'...
[14:26:15] Finished 'js' after 39 ms
[14:26:15] Finished 'html' after 49 ms
[14:26:15] Starting 'default'...
[14:26:15] Finished 'default' after 12 μs

In diesem Fall werden alle *.js Datei aus dem Verzeichnis app mit dem uglify-Package minimiert und dann nach dist geschrieben. Somit kann man also in dem app-Verzeichnis die Anwendung entwickeln und in dist erhält man immer alle Dateien, die man auf den Server kopieren muss.

Frontend-Entwicklung mit Node und bower

Immer häufiger greife ich für Anpassungen nicht mehr zu Visual Studio, sondern zu „einfachen“ Editoren, mit denen ich „mal eben“ ein paar Anpassungen in JavaScript machen – oder doch die eine oder Anwendung komplett in HTML & JavaScript schreibe.

Auch wenn man alles nur mit Notepad machen kann, so ist das doch irgendwie auf die Dauer etwas lästig. Immer wieder man man JavaScript Dateien auf den Server kopieren oder man muss Coffee-Script Dateien auf der Kommandozeile durch den entsprechenden Compiler jagen und dann mit den anderen Scripten zum Server kopieren.

Mit Notepad++ kann man das schon etwas verbessern, indem man Plugins wie NppExec verwendet. Wie man mit NppExec Dateien deployen kann, habe ich ja in einem früheren Post schon mal beschrieben. Aber irgendwann hat man auch dort Grenzen erreicht.

Lange habe ich mich gefragt, ob ich Node wirklich brauche. Ich finde JavaScript toll, aber muss ich das mit Node auch auf dem Server ausführen? Inzwischen habe ich erkannt: Node ist doch irgendwie total cool, gerade um HTML & JavaScript basierte Lösungen zu erstellen.

Im Folgenden will ich einmal einen Eindruck geben, wie so ein Projekt exemplarisch aussehen kann.

Zunächst muss man natürlich Node installiert haben. Das geht am besten via Chocolatey. Hat man Chocolatey installiert kann man mit choco install nodejs Node installieren.

Für unsere neues Projekt muss als erstes ein Arbeitsverzeichnis erstellt werden und das für die Arbeit mit Node vorbereitet werden

mkdir html_app
cd html_app
git init & npm init

Als Ergebnis erhält man eine package.json Datei.

{ 
    "name": "html_app", 
    "version": "1.0.0", 
    "description": "", 
    "main": "index.html", 
    "author": "Henning Eiben", 
} 

Diese Datei beschreibt das aktuelle Projekt/Paket und dient auch dazu um alle verwendeten Pakete zu speichern. Nun kann man mit npm sich Node Pakete installieren. Als erstes installiere ich bower. Das ist ein Paket um JavaScript-Bibliotheken für Anwendungen zu verwalten. Da ich Bower häufiger gebrauche installiere ich das gleich global.

npm install -g bower 

Nun kann ich mir mit Bower ein paar Bibliotheken für meine JavaScript-Anwendung laden. In diesem Fall will ich Bootstrap und Knockout verwenden. Zuvor initialisiere ich mit bower init noch eine bower.json. Sie dient ähnlich wie die packages.json dazu um alle verwendeten Pakate zu speichern. Mit

bower install bootstrap knockout --save 

Kann ich nun die beiden Bibliiotheken installieren. Durch das --save werden die Bibliotheken in der bower.json als Abhängigkeit gespeichert. Die bower.json sieht dann so aus:

{ 
    "name": "html_app", 
    "version": "0.0.0", 
    "authors": [ 
        "Henning Eiben <eiben@busitec.de>" 
    ], 
    "ignore": [ 
        "**/.*", 
        "node_modules", 
        "bower_components", 
        "test", 
        "tests" 
    ], 
    "dependencies": { 
        "bootstrap": "~3.3.5", 
        "knockout": "~3.3.0" 
    } 
} 

Dabei wurde neben bootstrap und knockout auch jquery in der Version 2.1.4 installiert, weil das von Bootstrap benötigt wird – ohne dass ich mich darum kümmern musste.

D:\projects\html_app>bower install bootstrap knockout --save 
bower cached git://github.com/twbs/bootstrap.git#3.3.5 
bower validate 3.3.5 against git://github.com/twbs/bootstrap.git#* 
bower cached git://github.com/SteveSanderson/knockout.git#3.3.0 
bower validate 3.3.0 against git://github.com/SteveSanderson/knockout.git#* 
bower cached git://github.com/jquery/jquery.git#2.1.4 
bower validate 2.1.4 against git://github.com/jquery/jquery.git#>= 1.9.1 
bower install knockout#3.3.0 
bower install bootstrap#3.3.5 
bower install jquery#2.1.4 
knockout#3.3.0 bower_components\knockout 
bootstrap#3.3.5 bower_components\bootstrap 
└── jquery#2.1.4 
jquery#2.1.4 bower_components\jquery 

Nun kann mit dem Editor der Wahl begonnen werden die Anwendung zu erstellen. Die Bibliotheken, die über bower installiert wurden liegen dabei in dem Verzeichnis bower_components, die Node-Pakete liegen in node_modules.

Wenn man nun die Anwendung z.B. in git einchecked oder jemand anderem zur Verfügung stellt, dann muss man diese beiden Verzeichnissen nicht mit weitergeben.

Stattdessen reicht es mit npm install & bower install einfach die in der packages.json und bower.json gespeicherten Pakete und Bibliotheken wieder herzustellen.

Debugging von Nintex Workflows

Das Debuggen von Workflows ist immer wieder eine Herausforderung, insbesondere im Umfeld von SharePoint. Die hauseigenen Workflows, die man mit dem SharePoint-Designer erstellen kann, lassen Funktionen für das Debugging nahezu ganz vermissen. Man kann hier ausschließlich in den Workflow-Verlauf Nachrichten schreiben um nachvollziehen zu können, welchen Weg ein Workflow während der Ausführung gegangen ist. Das ist aus mehreren Gründen “problematisch”:

Man muss sehr viele Nachrichten in den Verlauf schreiben, typischerweise gibt man dort den Wert von Variablen aus und beschreibt den Fortschritt. Diese Workflow-Schritte haben keine funktionale Bedeutung für den eigentlichen Prozess, kosten aber dennoch “Zeit”. Zudem dienen diese Schritte ja auch nicht dem eigentlichen Workflow und “verstopfen” somit den eigentlichen Prozess.

Als Programmierer ist man das “mehr” gewohnt. Wenn man nachvollziehen will warum sich ein Programm auf die eine oder andere Art & Weise verhält, dann “debuggt” man das Programm einfach. Währenddessen kann man sich den Inhalt von Variablen ansehen und somit nachvollziehen welchen Weg ein Programm genommen hat.

Bei Nintex geht das ebenfalls. Neben der visuellen Darstellung des Workflows, der den durchlaufenen Pfad farblich kennzeichnet (grün) und die aktuell ausgeführte Aktion hervorhebt (gelb) gibt es das sogenannte Verbose Logging.

Workflow-Verlauf

Beim Verbose Logging werden bei jeder Aktion die Werte von allen Variablen und den wichtigsten Workflow-Daten zu beginn der Aktion und am Ende der Aktion aufgezeichnet. Wenn man sich anschließend den Ablauf des Workflows ansieht kann man mit einem Klick auf eine Aktion sich all dieser Vorher/Nachher Werte anzeigen lassen. Änderungen, die während der Aktion an Werte vorgenommen wurden werden dabei ebenfalls farblich hervorgehoben.

Verbose Logging

Dabei ist allerdings zu beachten, dass diese Transparenz im Workflow durchaus ihren Preis hat. Das Aufzeichnen dieser ganzen Werte kostet Zeit und all diese Werte müssen in der Nintex-Datenbank gespeichert werden. Laufen sehr viele Workflow-Instanzen parallel, dann kann die Größe der Datenbank auch durchaus schnell anwachsen.

Um das Verbose Logging zu nutzen muss das in den Workflow-Einstellung des jeweiligen Workflows aktiviert werden. Somit werden nicht automatisch für alle Workflows diese Daten gesammelt, sondern man kann das pro Workflow individuell aktivieren. Zudem muss die Funktion auch noch global in der Zentraladministration einmalig aktiviert werden, zusammen mit der Angabe, wie lange die detaillierten Informationen gespeichert werden sollen.

Workflow-Einstellungen

Einstellung zum Verbose-Logging in der Zentraladministration

Wenn bspw. bei einem Workflow das Verbose Logging aktiv ist um den Ablauf auf einem Testsystem genau nachzuverfolgen und dieser Workflow anschließend ohne Anpassung auf ein Produktivsystem transportiert wird, dann muss das nicht zwangsläufig zu Leistungseinbußen führen, wenn auf dem Produktivsystem in der Zentraladministration die Funktion für das Verbose Logging nicht aktiv ist.

Datenbankzugriff in SharePoint 2013 mit BCS und SQL-Login

Mit dieser Anleitung will ich zeigen, wie man mit dem Secure Store in SharePoint auf Datenbanken über ein SQL-Login zugreifen kann.

Ausgangslage

Ich habe auf einem MS-SQL Server die wohl bekannte NorthWind Datenbank eingerichtet. Auf die Datenbank hat mein Benutzer busitec\eiben Zugriff und es gibt einen SQL-Benutzer mit dem Login „northwnd“.

Externer Inhaltstyp

Zunächst einmal muss der externe Inhaltstyp – wie von Simon auch schon beschrieben – angelegt werden.

image

image

Danach kann man im Vorgangsdesigner eine neue SQL-Verbindung angeben. Hier sind also der Name des SQL-Server (ggf. mit Instanz) und der Name der Datenbank anzugeben. Zudem kann der Verbindung optional noch ein eigene Name gegeben werden.

image

Zunächst wird hier für die Verbindung die Identität des Benutzers verwendet. In diesem Fall bin das also ich, also der Benutzer unter dem der SharePoint-Designer ausgeführt wird.

image

Nun werden alle Tabelle der Datenbank angezeigt. In diesem Fall erstelle ich aus dem Designer alle Vorgänge für die Tabelle „Customers“.

image

Alle Vorgänge bedeutet, dass Operationen zur Listenansicht (zum Lesen aller Elemente und Anzeige in der Listenansicht), zum Lesen eines einzelnen Elements (zur Anzeige in der Dispform oder Editform), zum Aktualisieren eine Elemente (nach der Bearbeitung in der Editform) und zum Erstellen eines neue Elements (durch die Newform) angelegt werden.

Nun können auf Basis dieses externen Inhaltstypes eine Liste und Formulare erstellt werden.

2015-03-20 10_10_41-Liste und Formular für _NorthWind Kunden_ erstellen

Ganz wichtig ist, dass auf dem neu erstellen BCS-Model noch keine Berechtigungen vergeben wurden. Somit kann bisher noch niemand auf die Daten zugreifen. Falls jemand bereits auf die Liste zugreift wird er eine Fehlermeldung von SharePoint erhalten. Also müssen in der Zentraladministration im Business Conectivity Service noch Berechtigungen für das Model vergeben werden.

2015-03-20 10_12_46-Select People and Groups - Internet Explorer

Wenn man nun im SharePoint auf die Liste zugreift, dann erhält man nicht wie erwartet die Liste der Kundendaten.

2015-03-20 10_13_37-Customers - Neuer externer Inhaltstyp (2) Liste lesen - Internet Explorer

Wenn wir noch einmal die Authentifizierungseinstellungen im SharePoint Designer ansehen, dann steht hier, dass der Zugriff auf die Datenbank durch die Windows-Identität des Benutzers erfolgen soll.

image

Das setzt voraus, dass Kerberos-(Constrained-)Delegation eingerichtet wurde. In meinem Fall haben aber nicht alle Benutzer Zugriff auf die Datenbank sondern ich möchten eine dedizierten SQL-Benutzer für die Zugriffe verwenden. Wie man an dem Dialog oben erkennen kann, habe ich hier aber keine Möglichkeit einen Benutzernamen oder Password anzugeben, geschweigen denn, dass ich den kompletten Connection-String angeben kann.

Secure-Store To The Rescue

Zunächst ändern wie die Authentifizierungseinstellungen auf Benutzerdefinierte Identität annehmen und definieren eine Anwendungs-ID für den Secure-Store (in diesem Fall „NorthWindSqlLogin“)

image

Anschließend erstellen wir in der Zentraladministration eine neue Secure-Store Anwendung mit eben diesem Namen „NorthWindSqlLogin“.

2015-03-20 10_16_20-Create New Secure Store Target Application

Im nächsten Schritt geben wir an, welche Felder im Secure-Store zu der Anwendung abgelegt werden sollen. Standardmäßig sind das die Felder Benutzer und Password, wobei das Passwort vom Typ „Windows Password“ ist. Wichtig: dass muss auf „Password“ geändert werden!

2015-03-20 10_16_49-Create New Secure Store Target Application

Anschließend kann noch ein Administrator für die Anwendung bestimmt werden – dieser kann später Benutzername und Passwort der Anwendung vergeben und angeben, wer alles diesen Secure-Store-Eintrag lesen kann.

2015-03-20 10_17_54-

Nun kann für den neuen Eintrag ein Benutzername und Passwort eingegeben und im Secure-Store gespeichert werden.

2015-03-20 10_19_48-

Finale

Beim abschließenden Zugriff auf die Liste werden wie erwartet die Kundendaten aus der NorthWind Datenbank angezeigt.

image

Damit die Anmeldung über das SQL-Login passiert ist es wichtig, dass in der Secure-Store Anwendung das Passwort als „Passwort“ und nicht als „Windows Passwort“ abgespeichert wird. Ansonsten würde eine Authentifizierung mit einem Windows-Account versucht werden – das mag ggf. auch gewollt sein, wenn man z.B. alle Zugriffe auf eine Datenbank über einen einzelnen Windows-Account machen will, aber in diesem Fall war ja explizit ein SQL-Login gewünscht.

Debuggen von SharePoint-Apps

Vor einiger Zeit wurde ich gefragt: “Wie kann ich eigentlich eine SharePoint(-Hosted) App debuggen?”.

Die erste Antwort ist natürlich: Developer Tools! Alle Browser verfügen heute über Developer-Tools, die das Debugging von JavaScript Anwendungen erlauben.

imageimage

Wenn ich die Anwendung im Browser ausführe kann ich in den Quellcode springen und dort – wie in Visual Studio – Breakpoints setzen. Anschließend muss ich die Seite ggf. noch einmal laden, damit der JavaScript-Code noch einmal ausgeführt wird und mein Breakpoint erreicht wird.

Anschließend kann ich den Wert von Variablen ansehen oder den Call-Stack betrachten und natürlich kann ich durch meinen Code-Steppen und genau die Ausführung meines Codes untersuchen.

image

Es gibt aber auch noch einen andere Weg: Visual Studio!

Wenn ich den IE als Browser für meine Anwendung verwende, dann kann ich meine Breakpoints auch direkt in Visual Studio in meinem JavaScript Code setzen. (Wenn man mehrere Browser installiert hat, habe ich in einem früheren Beitrag beschrieben, wie ich in einem SharePoint-App-Projekt den zu verwendenden Browser festlegen kann).

Anschließend kann ich meine Anwendung direkt in Visual Studio starten und sobald ich meinen Breakpoint erreiche kann ich meinen JavaScript Code in Visual Studio debuggen – mit alle Debugging-Funktionen die ich in Visual Studio zur Verfügung habe.

image

Ein Vorteil bei der Verwendung von Visual Studio ist, dass ich nicht zunächst die JavaScript Datei einmal im Browser geladen haben muss um den Breakpoint zu setzten.

Browser für SharePoint-Apps festlegen

Als Web-Entwickler bin ich es inzwischen gewohnt, dass ich in Visual Studio beim Starten meiner Web-Anwendung auswählen welcher Browser für die Anzeige verwendet werden soll.

image

Leider sieht das Start-Button bei einem SharePoint-Projekt ein wenig anders aus.

image

Es wird immer der Browser verwendet, den ich als Default-Browser in Windows eingestellt habe. Schade wenn ich die Anwendung mal mit einem anderen Browser testen will – dann muss ich den Browser manuell starten und die URL per Copy & Paste dort einfügen; insbesondere weil die URL bei SharePoint-Hosted Apps ja nun auch nicht gerade intuitiv ist.

Aber es gibt eine Lösung. Wenn ich die Projekteigenschaften anzeige, dann kann ich bei SharePoint-Projekten auswählen welcher Browser beim Starten verwendet werden soll.

image

Super!!

Dokumentsets im Eigenbau

Dokumentsets sind ein sehr schicke Funktion von SharePoint Server. Leider gibt es die nur in der Server-Version – also nicht wenn man nur Foundation einsetzt.

Was also tun, wenn der Kunde nur eine Foundation hat, aber Anforderungen hat, die nach den Eigenschaften eines Document-Sets schreien?

Die Anforderungen

Vielleicht zunächst zu den Anforderungen des Kunden. Die Anforderung war, dass es ein Dokument gibt, zu welchem Anlagen existieren. Also eine Klassischer Master-Detail Beziehung zwischen den Dokumenten.

SharePoint kennt ja durchaus Anlagen – aber nur für Listenelemente, nicht bei Dokumenten. und Anlagen habe den Nachteil, dass es keine Versionierung und eigene Meta-Daten gibt. Das ist irgendwie doof. Schöner wäre es, wenn man also zu einem Dokument beliebig viele Anlagen anfügen könnte.

Natürlich hat mir diese Frage keine Ruhe gelassen und ich habe mir gedacht, dass sich das doch recht einfach lösen lassen muss.

Ein Lösungspfad

Fangen wir einmal ganz einfach an. Zuerst einmal brauchen wir zwei Dokument-Bibliotheken, eine für die Dokumente und eine für die Anlagen. In der Bibliothek der Anlagen wird dann eine Nachschlage-Spalte hinzugefügt, die auf die Dokumente verweist. Somit kann man also Anlagen hochladen und die mit einem Dokument verknüpfen.

doc_set_diy_01

Als nächstes sollten die Anlage immer beim jeweiligen Dokument mit angezeigt werden. Dazu wir die Standard-Ansicht des Dokuments bearbeitet und ein App-WebPart für die Anlagen Hinzugefügt.

doc_set_diy_02

Nun muss an das WebPart noch ein Filter übergeben werden. Dazu wird die ID des aktuellen Dokuments übergeben und in der Liste der Anlage in der Spalte QM-Dokumente gefiltert.

doc_set_diy_03

Wenn man nun also ein Dokument öffnet, sieht man nur die Anlage, die mit diesem Dokument verbunden sind.

Allerdings hat diese Lösung noch einen Haken: sie ist unhandlich in der Pflege. Wenn man eine neue Anlage zu eine Dokument hinzufügen will, dann muss man die Anlage in die Liste der Anlagen hochladen und in den Meta-Daten auf das Dokument verweisen. Das ist irgendwie doof. Besser wäre es ja, wenn man z.B. in der Detail-Ansicht des Dokuments sagen könnte “bitte eine neue Anlage für dieses Dokument hochladen”.

Das Customizing

Mit ein wenig JavaScript kann man das Handling der Lösung vereinfachen. Zunächst wird jQuery, ein File-Input Element und ein Button gebraucht.

<script type="text/javascript" src="https://mytenant.sharepoint.com/qm/scripts/jquery-1.8.2.min.js"></script>
<input type="file" id="fileSelectorInput">
<input type="button" onClick="javascript: DoUpload();" value="Go!">

Nun folgt also die Magie. Die liegt hier natürlich in der Funktion DoUpload. Hier wird zunächst die Datei aus dem Dateisystem gelesen und dann als ByteCharacter-Array an eine weiter Funktion übergeben.

function DoUpload() {
    var fileInput = jQuery('#fileSelectorInput');

    var file = fileInput[0].files[0];
    var reader = new FileReader();
    reader.onload = function (result) {
         var fileName = '',
         libraryName = '',
         fileData = '';
 
        var byteArray = new Uint8Array(result.target.result)
        for (var i = 0; i < byteArray.byteLength; i++) {
            fileData += String.fromCharCode(byteArray[i])
        }
        
        DoUploadInternal(file.name, fileData);
    };
    reader.readAsArrayBuffer(file);
}

Soweit ist ja noch alles ganz Simple. Als nächstes wird die Datei mittels JSOM in den SharePoint geladen, um direkt auch die Meta-Daten setzen zu können.

Zunächst wird die ID des aktuellen Dokuments aus der URL gelesen und der Wert des Feldes QMName aus dem Anzeigeformular. Denn wir wollen der Anlage nicht nur einen Verweis auf das aktuelle Dokument geben, sondern auch die Meta-Daten aus dem Feld QMName mit an die Anlage übergeben.

JSRequest.EnsureSetup();
var qmName = jQuery('h3:contains("QM Name")').closest('td').next('td').text();
var qmDokumentId = JSRequest.QueryString["ID"];

Nun brauchen wir einen ClientContext, damit wir eine neue Datei für SharePoint anlegen können. Dort fügen wir dann das Character-Array unserer Datei als Content an.

clientContext = new SP.ClientContext.get_current();
var oWebsite = clientContext.get_web();
var oList = oWebsite.get_lists().getByTitle("QM-Anlage");

var fileCreateInfo = new SP.FileCreationInformation();
fileCreateInfo.set_url(fileName);
fileCreateInfo.set_content(new SP.Base64EncodedByteArray());

for (var i = 0; i < fileContent.length; i++) {
    fileCreateInfo.get_content().append(fileContent.charCodeAt(i));
}

newFile = oList.get_rootFolder().get_files().add(fileCreateInfo);
clientContext.load(newFile);

Nun müssen wir noch die Meta-Daten setzen. Dazu holen wir zunächst alle Feld-Informationen und setzen dann den Wert für QMName und erstellen einen Lookup auf das aktuelle Dokument.

item = newFile.get_listItemAllFields();
clientContext.load(item);

item.set_item("QMName", qmName);
var object = new SP.FieldLookupValue();
object.set_lookupId(qmDokumentId);
item.set_item("QMDokument", object);
item.update();

clientContext.executeQueryAsync(successHandler, errorHandler);

Am Schluss wird das Ganze dann als asynchrone Query an den Server gesendet. Im successHandler wird dann noch die aktuelle Seite aktualisiert, damit das neu hinzufügte Dokument auch in der Liste erscheint.

SP.UI.ModalDialog.RefreshPage(SP.UI.DialogResult.OK); 

Fazit

Mit ein bisschen Creativität und ein wenig JavaScript Anpassungen kann man schon ganz ordentlich etwas aus SharePoint heraus kitzeln.

doc_set_diy_04