SharePoint Bulk-Delete via PowerShell

1.000+ ListItems in einer SharePoint-Liste und die müssen alle weg – und zwar fix. Manchmal sind es Demo- oder Test-Daten und mal soll schlicht nur aufgeräumt werden. Was tun? Eine Art „TRUNCATE TABLE“ wäre schön. Wieder hilft hier die PowerShell mit wenigen Zeilen und geladenen SharePoint-Commandlets weiter.

DELETE ALL ListItems

$web = Get-SPWeb <URL zum Web>
$lst = $web.Lists[„<Name der Liste>“]
$lst.Items | % { $lst.GetItemById($_.ID).Delete(); }

Zwar muss man dediziert für jedes ListItem die Delete-Methode rufen doch dafür verschwinden so zügig und unkompliziert die nicht länger benötigten Elemente aus SharePoint-Listen oder Bibliotheken.

Noch etwas interessanter wird der auf den ersten Blick nicht übermäßig elegant anmutende Ansatz für ein selektives Massen-Löschen…

DELETE SOME ListItems

$web = Get-SPWeb <URL zum Web>
$lst = $web.Lists[„<Name der Liste>“]
$query = New-Object Microsoft.SharePoint.SPQuery
$query.Query = „<Where><Eq><FieldRef Name=’Project‘ LookupId=’TRUE‘ /><Value Type=’Lookup‘>23</Value></Eq></Where>“
$lst.GetItems($query) | % { $lst.GetItemById($_.ID).Delete(); }

Mit Hilfe einer CAML-Abfrage holt man sich nur die ListItems, die man löschen möchte. Auf dem SPList-Objekt findet man die Methode GetItems(), welches als Parameter ein SPQuery-Objekt entgegennimmt. Im Beispiel oben sind ListItems zu löschen, die mit einem Projekt-Datensatz in einer zweiten Liste verknüpft sind, welcher auf die ID 23 hört.

Wer nun Freude am Löschen per PowerShell findet, für den habe ich hier noch ein paar CAML-Query Beispiele zusammengetragen, welche vielleicht die ein oder andere Idee zur eigenen Adaption bringt…

# ** Query-Beispiel: ListItems, deren ID kleiner als 250 ist
$query.Query = „<Where><Lt><FieldRef Name=’ID‘ /><Value Type=’Counter‘>250</Value></Lt></Where>“

# ** Query-Beispiel: ListItems, die am oder nach dem 01.01.2016 angelegt wurden
$query.Query = „<Where><Geq><FieldRef Name=’Created‘ /><Value IncludeTimeValue=’FALSE‘ Type=’DateTime‘>2016-01-01</Value></Geq></Where>“

# ** Query-Beispiel: ListItems, die im Titelfeld mit ‚A‘ beginnen
$query.Query = „<Where><BeginsWith><FieldRef Name=’Title‘ /><Value Type=’Text‘>A</Value></BeginsWith></Where>“

# ** Query-Beispiel: ListItems, deren Beschreibungsfeld (‚Description‘) das Wort ‚Prozess‘ enthält
$query.Query = „<Where><Contains><FieldRef Name=’Description‘ /><Value Type=’Note‘>Prozess</Value></Contains></Where>“

# ** Query-Beispiel: ListItems, deren Aktiv-Flag auf FALSE gesetzt ist
$query.Query = „<Where><Eq><FieldRef Name=’Active‘ /><Value Type=’Boolean‘>0</Value></Eq></Where>“

Und wem das nicht weit genug geht – man kann nicht nur die Delete-Methode auf dem Objekt rufen…

LED Strip Connector mit eigenem Kabel anschließen

Heute mal thematisch etwas ganz anderes aus der Kategorie „Basteln“. Niedervolt LED-Strips haben hervorragende Eigenschaften, um mit minimalem Aufwand als Hintergrundbeleuchtung verbaut zu werden. Die Installation ist denkbar einfach und das Anschließen der Stromversorgung ist selbst für Hobby-Handwerker keine echte Herausforderung, wenn die Strips nicht gerade im Sichtfeld montiert werden sollen und man die passenden Verbinder zur Hand hat.

Kürzlich hatte ich das Problem, dass ich LED-Strips anschließen wollte, bei denen aus ästhetischen Gründen der Anschluss direkt in ein weißes Kabel übergehen sollte, welches ich sichtbar aber dezent am Türrahmen verlegen wollte. Im Lieferumfang fanden sich ein dickes, schwarzes Kabel mit vergossener Sockel-Buchse, eine etwas dünnere und angelötete Doppellitze in Rot und Schwarz sowie zwei Schnellverbinder, mit denen man hätte zwei Strips verbinden können. Grundsätzlich ausreichend – in meinem Fall allerdings keine brauchbaren Optionen, wenn nicht von den Türrahmen schwarze Kabelpeitschen mit Steckern baumeln sollten.

So fiel meine Wahl auf die Schnellverbinder, die eigentlich nicht zum Anschluss der Stromversorgung gedacht waren. Bleibt die Frage: Wie die Litze so einklemmen, dass sie auch nach dem Schließen des Schnellverbinders noch ausreichend Kontakt gibt, keinen Kurzschluss macht und nicht verrutscht?

LED-con_before

Nach einigen erfolglosen Versuchen, war ich bereits fast willens die Anschlüsse direkt auf die LED-Strips zu löten. Da kam mir die Idee, direkt aus den Kabelenden einen Verbinder zu formen, der unter die beiden Kontakte passt und nicht verrutscht.

LED-con_prep1

Die von der Isolierung befreiten Enden zog ich etwas auseinander, so dass die Litze ein flaches und breites Bündel bildete (1). Anschließend verzinnte ich die Bündel mit ausreichend Flussmittel (2). Trotz des vorherigen Auseinanderziehens waren die Enden noch viel zu dick, um unter die Kontakte zu passen.

LED-con_prep2

Um die Enden nun entsprechend flach zu formen, presste ich die Enden mit Hilfe eines Feinmechaniker-Schraubstocks auf die benötigte Stärke, in etwa einen viertel Millimeter und schnitt anschließend überschüssiges Material ab, um die Kontaktflächen zu bilden (3). Wer keinen Schraubstock zur Hand hat, könnte auf eine ausreichend starke Zange, Schraubzwingen oder im Notfall auch auf einen Hammer zurückgreifen. Um nun unter den Kontakten nicht zu verrutschen, hilft noch eine leichte Körnung in der Mitte der entstandenen Kontaktfläche (4).

Das Ergebnis erfüllt seinen Zweck und ist in jedem Fall eine Alternative, wenn man gerade kein passendes Anschlussmaterial sondern nur den Schnellverbinder bei der Hand hat.

LED-con_after

Sicherheitshalber noch der Hinweis, dass sich diese Art des Anschlusses lediglich für feste, nicht bewegte Installationen sowie für die 2-Pin-Variante sinnvoll anbietet, da nur der zweipolige Schnellverbinder (10mm) über ausreichend Platz zur Aufnahme der gelöteten Kontaktflächen bietet.
Obwohl auf Grund der geringen Spannung die Luftstrecke vernachlässigbar ist, sollte ein Abstand von mindestens einem Millimeter zwischen den Kontakten eingehalten werden, um Fehler zu vermeiden. Und zu guter Letzt: Jeder bastelt auf eigene Verantwortung. Gewissenhaftes Arbeiten bei der Herstellung und Klemmung der Kontakte ist dementsprechend wichtig – wer sich unsicher ist, sollte auf Anschlussmaterial der Hersteller zurückgreifen.

RSSharePointList-CAML-Hacks

Möchte man mit wenig Aufwand auf SharePoint-Listen mit Hilfe der SQL Server Reporting Services zugreifen, so kommt man am Datenquellen-Verbindungstyp für SharePoint-Listen nicht vorbei.
Dieser Verbindungstyp, auch bekannt als RSSharePointList bzw. SharePoint List Connection Type (Data Extension/Data Provider), ermöglicht einen einfachen Zugriff auf einzelne SharePoint-Listen eines SharePoint-Webs.

rssplist_smpl1

Konkret wird dabei ein SharePoint-Web bzw. eine SiteCollection mit einer SSRS-DataSource abgebildet, wohingegen ein SSRS-DataSet eine SharePoint-Liste repräsentiert.

rssplist_smpl2

Mittel eines Abfrageassistenten, der alle Listen/Bibliotheken eines Webs auflistet und darin enthaltene Spalten für das zu erstellende DataSet zur Auswahl anbietet, ist eine Definition der Reportdatenquelle schnell zusammengeklickt.

rssplist_smpl3

Heraus kommt eine XML-Struktur, eingeleitet mit dem Tag <RSSharePointList>, welches dem Entwickler mit SharePoint-Erfahrung bereits vertraut vorkommen könnte. Spätestens dann, wenn man noch eine Abfragebedingung im Assistenten hinzufügt, sind die Parallelen zu CAML, der in der SharePoint-Welt gängigen Collaborative Application Markup Language, nicht zu leugnen.

So weit, so gut. Solange man mit dem auskommt, was der Abfragedesigner zu leisten vermag, ist der BI-Analyst zufrieden. Letztendlich ist das oben Beschriebene ein alter Hut und die Nachfolgetechnologien stehen lange in den Startlöchern. Dennoch – SSRS und SharePoint sind auch 2016 noch ein Thema und es gibt Situationen, in denen man schneller als erwartet an Grenzen stößt. Ganz besonders dann, wenn man an den Komfort von der Flexibilität von SQL-Statements gewohnt war, um die Daten in eine für den Report sinnvolle Form zu bringen.

Hier wird es eng…

  • Sortieren von DataSets bereits zur Abfrage (Äquivalent zum SQL ORDER BY)
  • Arbeiten mit Referenzen auf andere SharePoint-Listen bzw. Listenelemente
  • Arbeiten mit Referenzen aus MultiValue-Lookups

Das grundlegende Problem wird bereits im Abfragedesigner deutlich. Eine Abfrage, ein DataSet. Sobald eine zweite Liste ausgewählt wird, erinnert eine Messagebox daran, dass die bisherige Auswahl verworfen wird. In nativem CAML wären JOINs möglich – nicht so mit RSSharePointList. Benötigt man nun Daten des referenzierten Datensatzes aus einer zweiten Liste, ist man darauf angewiesen, die zweite Liste ebenfalls als DataSet einzubinden und mittels Lookup-Funktion der Reporting Services dort einzelne Spalten abzuholen. Dies wird zudem noch erheblich erschwert, dass man mit dem Anzeigenamen des referenzierten Datensatzes und nicht mit der ID arbeiten muss, dass die ID hinter dem SharePoint Lookup-Feld nicht im DataSet erscheint.

Hier kommt man weiter…

Sortieren

Glücklicherweise ist im Rahmen von Berichtselementen wie Diagrammen oder Tabellen bzw. Tabellengruppen das Sortieren vorgesehen, so dass man selten die Notwendigkeit hat, dies bereits zur Abfragezeit zu berücksichtigen. Bei Parametern allerdings, deren Daten auf einer Abfrage basieren, ist dies der einzige Weg, da das DataSet selbst keine Sortierung kennt.
Da uns die Ähnlichkeit zu CAML aufgefallen ist, ist es naheliegend das Sortier-Konstrukt aus eben dieser Notation im SSRS Data Provider zu testen. Unterhalb des Query-Elements kann man das OrderBy-Element verwenden…

<RSSharePointList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ListName>SortTest</ListName>
  <ViewFields>
    <FieldRef Name="Title" />
    <FieldRef Name="Category" />
    <FieldRef Name="DueDate" />
    <FieldRef Name="ID" />
  </ViewFields>
  <Query>
    <OrderBy>
      <FieldRef Name="Category" />
      <FieldRef Name="DueDate" Ascending="FALSE" />
    </OrderBy>
  </Query>
</RSSharePointList>

Referenzen

Hat man innerhalb von SharePoint seine Datenstrukturen rein relational und vollständig 1:n aufgebaut, sprich ohne die Verwendung von Multi-Lookup-Spalten, so will man häufig nur eine Liste anhand einer ID filtern, die aus z.B. einem Report-Parameter stammt oder einem Übergabeparameter für einen Sub-Report dient.
Im Standard kommt man mit der allerdings nicht besonders weit, denn das Where-Element enthält nur einen einfachen, String-basierten Vergleich, der lediglich das Filtern auf Anzeigenamen ermöglicht. Sicherlich ist dies eine Alternative, solange der Titel sich nie ändert und über die gesamte Zeit eindeutig ist. Von diesen idealen Bedingungen kann man aber nicht immer ausgehen.
Ein einfacher Trick aus der CAML-Welt hilft hier weiter: Das Setzen des Attributs LookupId auf TRUE am FieldRef-Element im Query ermöglicht das Nachschlagen per ID.

<RSSharePointList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ListName>Task</ListName>
  <ViewFields>
    <FieldRef Name="Title" />
    <FieldRef Name="ProjectName" />
    <FieldRef Name="ScheduledDate" />
    <FieldRef Name="ActualDate" />
    <FieldRef Name="ID" />
  </ViewFields>
  <Query>
    <Where>
      <Eq>
        <FieldRef Name="ProjectName" LookupId="TRUE" />
        <Value Type="Lookup">
          <Parameter Name="ProjectId" />
        </Value>
      </Eq>
    </Where>
  </Query>
</RSSharePointList>

Innerhalb von Listen mit Referenzen auf andere Listen steht man vor einem ähnlichen Problem. Möchte man hier die ID zum referenzierten Datensatz erhalten, hat man nur den zum Nachschlagefeld definierten DisplayName. Dieser hilft ebenfalls nur unter idealen Bedingungen weiter und dürfte sich nie ändern. Und auch hier: Entgegen der CAML-Dokumentation führt das LookupId-Attribut am FieldRef-Element im ViewFields-Element zum Erfolg.

<RSSharePointList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ListName>Task</ListName>
  <ViewFields>
    <FieldRef Name="Title" />
    <FieldRef Name="ProjectName" LookupId="TRUE" />
    <FieldRef Name="ScheduledDate" />
    <FieldRef Name="ActualDate" />
    <FieldRef Name="ID" />
  </ViewFields>
</RSSharePointList>

…leider mit der Einschränkung, dass man sich für ID oder DisplayName des Lookups entscheiden muss.

…und hier hört es auf

  • CAML-Join
  • CAML-Group
  • CAML-RowLimit
  • Multi-Lookup Columns

rssplist_failed

Frohes Reporten weiterhin!

Homematic Notification Service

Ganz heimlich, still und leise hatte ich hier an dieser Stelle zum vergangenen Weihnachtsfest ein Stückchen Software in die große, weite Welt entlassen und nicht viele Worte darüber verloren: den Benachrichtigungsdienst für die Homematic CCU2.

Wofür dieser Benachrichtigungsdienst gut ist, ist schnell erklärt. Wer ein Heimautomatisierungssystem aus der Produktion der eQ-3 AG sein Eigen nennt, hat oft mehr als nur zeitgesteuerte Beleuchtung oder Heizungsregelung im Sinn. Ist man nicht zu Hause, ist eine Alarmschaltung oder ein Monitoring der Rauchmelder nützlich. Leider ist die Einrichtung einer zeitgemäßen Benachrichtigung über Änderungen von Schaltzuständen, zum Beispiel beim Öffnen einer Tür oder Erkennung von Bewegung über einen Bewegungsmelder, mit Bordmitteln nicht ganz trivial zu realisieren und mündet gelegentlich in eine instabile oder hängengebliebene CCU2. Um hier Abhilfe zu schaffen, habe ich mir zum Thema ein paar Gedanken gemacht und eine einfache Dienstanwendung gebastelt.

Das eigentliche Highlight ist die Möglichkeit zu haben, sich bei wichtigen Ereignissen per Push-Message direkt auf das Handy benachrichtigen zu lassen – hier im Beispiel die App Pushalot unter Windows 10 sowie auf dem Handy unter Windows Phone 8.1:

Die Anwendung ist als Windows-Dienst implementiert, versendet Benachrichtigungen für Ereignisse der per Konfiguration angegebenen Homematic-Sensoren und protokolliert auftretende Fehler im Eventlog. Kurz gesagt: reine Backend-Logik. Um dennoch die Funktion der Anwendung sowie der erstellten Konfigurationsdatei testen zu können, gibt es zusätzlich eine Windows-Forms Applikation, die sowohl die aus der Konfiguration ausgelesenen Einstellungen und Benachrichtigungselemente darstellen kann, als auch auszuführende Benachrichtigungen versendet/simuliert und Fehler protokolliert.

HomeMaticNotification_TST

Die Basiskonfiguration (Pfad zur Konfigurationsdatei, Ausführungsintervalle, etc.) des Dienstes oder auch der Anwendung ist in der jeweiligen App-Config „SVC_HomeMaticNotification.exe.config“ zu finden. Die wesentlich wichtigere, inhaltliche Konfiguration wie Benachrichtigungsgruppen, Benachrichtigungsziele sowie die zu überwachenden Geräte sind in einer separaten XML-Konfigurationsdatei zu konfigurieren.

HMN_SampleConfig

Um nicht nur einen Empfänger von einer Dienst-Instanz zu benachrichtigen, können parallel mehrere Gruppen von Benachrichtigungen (notifyGroup) mit unterschiedlichem Umfang, Einstellungen und Empfänger hinterlegt werden. Pro Gruppe kann ein Benachrichtigungsziel (notifyBy) festgelegt werden, was entweder ein Key der Push-Dienste Pushalot (NotifyProvider=“Pushalot“) oder Pushover (NotifyProvider=“Pushover“) sein kann oder auch eine E-Mail-Adresse (NotifyProvider=“Email“).
Für ein Benachrichtigungselement (notifyItem), ein per Device-Serial adressiertes Gerät, können erweiterte Einstellungen konfiguriert werden, so dass beispielsweise zu festgelegten Tagen/Uhrzeiten (silenceTimes/silence) oder bei dem Zutreffen bestimmter Bedingungen (conditions), wie Schaltzuständen weiterer Geräte (devCondition) bzw. Systemvariablen der Homematic-Zentrale (varCondition), Benachrichtigungen nicht übermittelt werden dürfen.

Das Monitoring der Homematic-Komponenten erfolgt in der vorliegenden Version nicht wirklich ereignisgesteuert, sondern bedient sich einem design-technisch nicht allzu schönem Workaround, der dies lediglich so erscheinen lässt. Allem voran die in meinem letztjährigen Post zum Thema erwähnten Schwierigkeiten mit der Eventregistrierung über die native Schnittstelle der CCU2, XML-RPC, führten dazu, dass ich ein – in der Standardkonfiguration zwei-sekündlich – „pollendes“ Verfahren für die Überwachung der Komponenten verwende. Auch wenn ich inzwischen teilweise die ursprünglichen Probleme umgehen konnte, hatte ich dennoch mit erhöhtem Stromverbrauch der abgefragten Sensoren/Aktoren zu kämpfen. Hinzu kommt, dass ich zu Beginn der Entwicklungsarbeiten am Dienst auf die HomeMatic-XmlApi-Lib zurückgreifen musste, so dass ein Rückportieren auf XML-RPC aus heutiger Sicht nicht lohnenswert erscheint. Die sehr häufige Abfrage der XML-API (~40k Requests/Tag) scheint keine negative Auswirkung auf die CCU2 zu haben – der Service läuft bei mir ca. seit einem Jahr sehr stabil ohne auffällige Probleme oder Unzuverlässigkeit.

Im aktuellen Release (v1.1.9.3) sind inzwischen ein paar neue Funktionen enthalten sowie ein paar Bugs korrigiert. Für alle Interessierten steht die Software (inkl. Tester-Anwendung und Beispielkonfiguration) in meinem OneDrive zum Download bereit. Die jeweils neuste Version wird als Quellcode im dazugehörigen Repository auf Github zu finden sein.

HomeMaticNotification_TSTGitHub-Repo: github.com/roschinsky/HomeMatic-Notification-Service

MD5 Hash: ca29cbe561e53507db4feca121454af6


Feedback, Forks oder interessante Fragen – hier oder auf Github – sind stets willkommen.

Für die Zukunft ist geplant die Dienstanwendung auf den RaspberryPi zu portieren, so dass sie unter Windows 10 for IoT lauffähig ist.

Frohes Fest!

Zum Feste nur das Beste! Open-Source darf auch unter dem Weihnachtsbaum nicht fehlen, so dass ich meine liebe Leserschaft pünktlich zum 24. mit vier Projekten erfreuen möchte, die dieses Jahr den Weg in GitHub-Repositories gefunden haben.

Homematic

Im Blog-Post aus dem März mit dem Erfahrungsbericht zur Homematic hatte ich über meine Ideen zur programmatischen Erweiterung des Systems geschrieben. Daraus entstanden sind eine C# Library, die den Zugriff auf das Homematic Add-On XML-API für .NET-Projekte kapselt, sowie ein Windows-Dienst zur Benachrichtigung über Statusänderungen von in einer Konfigurationsdatei definierten Homematic-Sensoren…

HomeMatic-Notification-Service

MD5 Hash: ca29cbe561e53507db4feca121454af6

 

HomeMatic-XmlApi-Lib

MD5 Hash: 6004e7a70784ac53e18f6da634897696

 

PushMonitoring

Ein minimalistisches Server-/Client-Monitoringsystem (Details siehe Blog-Post aus dem Juni)

MD5 Hash: 5aeefc81641179f80e416c07f217209b

 

SPWood

Ein Code-Generator für Feldnamen von SharePoint-Listen und -Bibliotheken (Details siehe Blog-Post aus 2014) zur Verwendung in eigenem Code als Konfigurationsklasse mit Enums.

MD5 Hash: 05f12753c0cbfd30f6eee9166cfe6954

 


Viel Spaß beim Ausprobieren und ein frohes Weihnachtsfest!:)

SharePoint 2013 Workflow mit ApplicationException HTTP 401

Kein gänzlich neues Thema: Ein SharePoint-Workflow bleibt hängen. Beim Testen ging er noch und nun, wo er produktiv gehen soll, läuft er nicht länger durch. Nur warum?

Dem Log oder auch den Workflow-Informationen entnimmt man ein vielsagendes „Angehalten“  bzw. „Suspended“. Bei genauerem Hinsehen (MouseOver des kleinen, blauen Info-Icon) findet man in etwa die folgende Fehlerbeschreibung und ist nur ein ganz klein wenig schlauer:

RequestorId: 472ce13d-a3c0-e24a-0000-000000000000.
Details: An unhandled exception occurred during the execution of the workflow instance. 
Exception details: System.ApplicationException: HTTP 401
{"Transfer-Encoding":["chunked"],
 "X-SharePointHealthScore":["0"],
 "SPRequestGuid":["472ce13d-a3c0-e24a-af05-003a17628b61"],
 "request-id":["472ce13d-a3c0-e24a-af05-003a17628b61"],
 "MicrosoftSharePointTeamServices":["15.0.0.4561"],
 "X-Content-Type-Options":["nosniff"],
 "Date":["Wed, 19 Aug 2015 23:42:01 GMT"],
 "Server":["Microsoft-IIS\/8.5"],
 "WWW-Authenticate":["NTLM"],
 "X-AspNet-Version":["4.0.30319"],
 "X-Powered-By":["ASP.NET"]}
  at Microsoft.Activities.Hosting.Runtime.Subroutine.SubroutineChild.Execute(CodeActivityContext context) 
  at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager) 
  at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)

Der Grund, warum ich ausgerechnet diesem Fehler einen Beitrag widme, ist der, dass im Netz zwar zahlreiche Fragen zur Fehlermeldung existieren aber unangenehmerweise fast ebenso viele Antworten, die nicht zwangsläufig zum Ziel führen. Man solle die Benutzerprofilsynchronisierung manuell in der Zentraladministration starten, man möge dem Workflow-ausführenden Benutzer Schreibrechte auf die korrespondierende Liste geben, man müsse statt SharePoint-Benutzer besser AD-Benutzer direkt berechtigen, ein Workflow-Identitätswechselschritt oder der neue App-Step soll Wunder wirken, und zahlreiche Ideen mehr. Leider in meinem konkreten Fall alles Käse, viel zu kompliziert gedacht und komplett vorbei an der Lösung…

Das Problem

Was man schon mal mit Gewissheit sagen kann, ist der Fakt, dass hier ein Berechtigungsproblem vorliegt. Die spannende Frage ist: Wo? Denn genau das, verrät die Meldung leider nicht.

Bevor ich das Rätsel lüfte, möchte ich ein paar signifikante Eigenheiten des Workflows sowie Konfiguration/Umgebungsbedingungen der SharePoint-Site beschreiben, da diese mit der Schlüssel zur Lösung sind:

  • Der „normale“ Benutzer auf der SharePoint-Site ist lediglich Mitglied der Besuchergruppe, hat folglich auf die meisten Elemente der Seite nur lesenden Zugriff
  • Der Workflow sollte ein Listenelement schreiben, was der den Workflow ausführende Benutzer von Hand auch kann/darf, da für die Liste die Berechtigungsvererbung aufgehoben wurde und die Mitglieder der Besuchergruppe hier schreiben dürfen
  • Der Workflow läuft ohne Probleme für Benutzer, die über die Mitgliedergruppe der Seite berechtigt sind, nicht jedoch für Benutzer der Besuchergruppe; dennoch sind die Rechte für Besucher und Mitglieder auf der in Rede stehenden Liste identisch
  • Der Workflow schlägt für Benutzer der Gästegruppe bereits relativ weit am Anfang fehl, noch bevor tatsächlich eine Create-Action für die Liste ausgeführt wird sondern lediglich beim Protokollieren im Workflowverlauf

Die Lösung

Der letzte Punkt in der Aufzählung ist der Entscheidende und das Schlüsselwort ist „Workflowverlauf“. Die Workflowverlaufsinformationen werden in der Standardkonfiguration eines Workflows ebenfalls in eine Liste auf der Seite geschrieben, die von Hause aus versteckt ist und dementsprechend nicht unter „Alle Websiteinhalte“ erscheint.
Mit Hilfe des SharePoint Designer 2013 oder auch des direkten Aufrufs (http://<server>/<site>/Lists/Workflow%20History/) erhält man Zugriff auf die Liste und sollte hier sicherstellen, dass den Benutzern, die den Workflow ausführen und damit in ihrem Namen in die Workflowverlaufsliste schreiben sollen, hier über die entsprechenden Rechte verfügen. Alternativ ist es natürlich auch möglich, eine neue Workflowverlaufsliste für den Workflow zu definieren – die Hauptsache ist, dass der Benutzer darin schreiben darf.

Noch ein Tipp zur Fehleranalyse sowie auch für den Fall, dass der beschriebene Lösungsansatz nicht zum Erfolg führt: Wenn man das Log bzw. die Workflow-Informationen direkt zur Laufzeit des Workflows beobachtet, erhält man möglicherweise andere Fehlermeldungen, die sich hinter dem blauen Info-Icon verbergen deutlich aufschlussreicher sind. So zum Beispiel brachte mich die folgende Meldung auf den richtigen Pfad zur Lösung meines Problems:

Retrying last request. Next attempt scheduled in less than one minute. 
Details of last request: HTTP Unauthorized to http://sitename/_vti_bin/client.svc/web/lists/getbyid(guid'guid')/Items 
Correlation Id: guid Instance Id: guid

(In Deutscher Umgebung: „Die letzte Anforderung wird wiederholt. Der nächste Versuch ist in weniger als einer Minute geplant“)

Mit der GUID, die sehr wahrscheinlich in einem der Selektoren des API-Requests enthalten ist, wie zum Beispiel der Listen-ID, lässt sich mit wenig Aufwand die Liste oder das Element, welches das Problem verursacht, schnell ausfindig machen.

SharePoint Server 2016 Reversed for Developers

TopChangesUnd noch immer kein SDK. Wie schade! Auch nach rund anderthalb Monaten findet man fast ausschließlich die bereits bekanntgegebenen Informationen, die eine Flut von fast immer gleichen Blog-Posts zum Thema nach sich zog. Selten machen sich die Autoren dabei die Mühe die 2016-Feature-Liste nicht nochmals 1:1 aufzuwärmen.
Viel interessanter wäre es doch zu sehen, was es alles Neues in der API gibt. Neue Methoden oder Eigenschaften auf den vertrauten Objekte wie SPList, SPWeb, SPFile und SPSite oder generell die Möglichkeiten per CSOM mit den neuen Compliance Features zu arbeiten. Selbst der reine Umfang an Änderungen bei den PowerShell Commandlets sollte interessante Rückschlüsse auf die strategische Ausrichtung von SharePoint aus dem Blickwinkel des Herstellers zulassen…

Glücklicherweise lassen sich die meisten Dinge mit Hilfe der verfügbaren Preview von SharePoint 2016 relativ einfach selbst recherchieren. Da ich mir den Spaß gegönnt habe, die Preview mit ihrer Vorgängerversion zu vergleichen, wollte ich es mir nicht nehmen lassen, einige der gewonnenen Erkenntnisse kurz niederzuschreiben.

PowerShell

Wohl am einfachsten ist es, sich die Verteilung der Commandlets hinsichtlich ihrer Aufgaben anzuschauen. Hierfür vergleiche ich lediglich die Anzahl aller SharePoint-Commandlets gruppiert nach Verb, was uns 17 Neuerungen bei den Get- und 16 bei den Set-Commandlets beschert. Rein prozentual sind die 4 neuen Reset-Commandlets noch zu erwähnen sowie auch die ganz neu eingeführten Migrate-Commandlets.

Changes_PowerShell

15-Hive vs. 16-Hive

Ähnlich simpel, jedoch noch wesentlich spannender wird es, wenn wir SharePoints 15-Hive dem 16-Hive gegenüberstellen. Nach einem Vergleich der Anzahl aller Files gruppiert nach Dateierweiterung ist schnell zu erkennen, wo sich wohl etwas getan haben muss.

compare_ISAPI-Folders

Die Webservices (.asmx- und .svc-Dateien) sind offenbar identisch von ihrer Anzahl her – die DLLs zeigen jedoch einen deutliche Unterschied. Schauen wir uns als die DLLs an. Aber nicht nur die Anzahl sondern auch die Größe der Dateien in der Gegenüberstellung beider ISAPI-Folder lässt Vermutungen zu, wo sich die scheinbar signifikantesten Änderungen verstecken.

Changes_LibrariesFür den Anfang gibt der Vergleich der beiden ISAPI-Folder Hinweise auf die Ausrichtung von künftigen SharePoint-Versionen sowie einen guten Einstieg, mit welchen Bibliotheken wir uns später ein wenig genauer befassen sollten. Das Augenscheinlichste jedoch zuerst – wer geht und wer kommt?

Vermutlich ausrangiert werden…

  • Microsoft.Office.Client.Education.dll
  • Microsoft.Office.Excel.Server.Udf.dll
  • Microsoft.Office.Excel.Server.WebServices.dll

Neu hinzu gesellen sich…

  • Die Portables, welche eine Art Repräsentation der vorhandenen Bibliotheken für den Einsatz auf leichtgewichtigeren Plattformen gedacht zu sein scheinen
    • Microsoft.Office.Client.Policy.Portable.dll
    • Microsoft.SharePoint.Client.DocumentManagement.Portable.dll
    • Microsoft.SharePoint.Client.Portable.dll
    • Microsoft.SharePoint.Client.Publishing.Portable.dll
    • Microsoft.SharePoint.Client.Runtime.Portable.dll
    • Microsoft.SharePoint.Client.Search.Applications.Portable.dll
    • Microsoft.SharePoint.Client.Search.Portable.dll
    • Microsoft.SharePoint.Client.UserProfiles.Portable.dll
    • Microsoft.SharePoint.Client.WorkflowServices.Portable.dll
  • Plattformspezifische Runtime-Versionen mit Store-Anbindung
    • Microsoft.SharePoint.Client.Runtime.Windows.dll
    • Microsoft.SharePoint.Client.Runtime.WindowsPhone.dll
    • Microsoft.SharePoint.Client.Runtime.WindowsStore.dll
  • Eine Project Server Bibliothek: Microsoft.ProjectServer.Client.dll
  • Eine wenig sagende Directory-Bibliothek: Microsoft.SharePoint.Client.Directory.dll

Noch ein wenig genauer hingeschaut

Wie schon angedeutet geben die Dateien mit dem größten Zuwachs ein gutes Ziel für einen direkten Vergleich der Code-Basis ab, um einen Eindruck zu gewinnen, was den SharePoint-Entwickler künftig erwarten wird.

Rein prozentual gegenüber den Dateien von SharePoint 2013 führen die Bibliotheken Microsoft.Office.Client.Policy.dll (259%), Microsoft.Office.Server.UserProfiles.dll (160%), Microsoft.SharePoint.Client.dll (138%) und Microsoft.Office.Policy.dll (136%) die Liste an – in absoluten Zahlen sind es Microsoft.SharePoint.dll (+4,08 MB), Microsoft.Office.Server.Search.dll (+3,10 MB), Microsoft.Office.Server.UserProfiles.dll (+1,74 MB) und Microsoft.SharePoint.Publishing.dll (+0,64 MB).

Microsoft.Office.Client.Policy.dll

Naheliegend, dass wir hier einen Großteil der Neuerungen rund um die neuen Compliance-Features von SharePoint finden. Microsoft hat hierfür den Namespace Microsoft.SharePoint.Client.CompliancePolicy mit Leben gefüllt, in der sich 15 Klassen und 6 Enums befinden.

compare_SP2013_OfficeClientPolicy compare_SP2016_OfficeClientPolicySpäter wird man vermutlich für Container(Website-Sammlungen, Websites und Listen/Bibliotheken) mit Hilfe von Associations und/oder Bindings vordefinierte Compliance-Richtlinien in Form von Rules und Definitions verknüpfen können. Hier ein wenig tiefer einzutauchen scheint auf jeden Fall vielversprechend für ein tieferes Verständnis des neu geschaffenen Konstruktes.

Microsoft.SharePoint.Client.dll

Der Namespace für alle, die viel mit dem CSOM arbeiten. In absoluten Zahlen nur noch auf Platz 9 der aktualisierten Libraries, relativ immerhin noch auf Platz 3. Die sehr zentrale Klasse ClientContext wächst wenig spektakulär von 281 auf 312 LOC und hat kaum mehr als eine neue öffentliche Eigenschaft namens RequestResources gleichen Typs, welcher ebenfalls als neue Klasse enthalten ist. Hier können wohl eine Liste und ein View per GUID referenziert werden sowie per bools auf die UserInformationList, auf WebProperties, Features oder Navigationsstruktur verweisen.
Dahingegen beispielsweise kommt die Klasse File von 821 vs. 1073 LOC und bringt einige neue Methoden mit.

compare_SP2013_Client_File.cs compare_SP2016_Client_File.cs

Hier ist unter anderem zu sehen, dass es offenbar nun Möglichkeiten zum asynchronen oder wenigstens partiellen Hochladen von Dateien zu geben scheint.

Microsoft.SharePoint.Client.Directory.dll

Eine der Neuen im Bunde… im Namespace Microsoft.SharePoint.Client.DirectoryInterface finden sich Klassen, die sich um das definieren oder gründen von Gruppen, das Beitreten zu Gruppen, das Verwalten von Gruppenmitgliedern sowie der Kommunikation zwischen Gruppenmitgliedern drehen. Noch habe ich im Frontend dazu nichts entdecken können…

Fazit

Rein auf Grund des Umfangs des Codes, allem voran der der Kernbibliothek Microsoft.SharePoint.dll, ist ein umfassender und flächendeckender Vergleich des Codes ein größeres Projekt oder bedarf eines Analysewerkzeugs. Definitiv lohnend ist ein gezielter Blick auf bestimmte Code-Fragmente, welche einen ganz brauchbaren Eindruck der Unterschiede zu vermitteln scheinen.

Insgesamt fügt sich alles eigentlich ganz gut zu dem, wie sich auch das Frontend gab: keine überwältigenden Neuigkeiten, hier und dort ein wenig am Schulterschluss zwischen Cloud und On-Premise gefeilt, Integration der neuen Kernfeatures sowie ein paar Altlasten entsorgt.

Erwähnenswert finde ich auch den Fakt, dass die Dokumentation des Codes, der hier von Version v15.0.4571 bzw. v15.0.4727 auf die unter die Lupe genommene Preview als Neuerung hervorgegangen ist, zumindest zum Teil bereits in der MSDN – geführt unter SharePoint 2013 aber „Last modified: October 05, 2015“ – zu finden ist. Das sogar mit gleichlautenden DLL-Referenzen, in welchen der Code nachweislich nicht enthalten war.

Folgen

Erhalte jeden neuen Beitrag in deinen Posteingang.

Schließe dich 172 Followern an