Eine Ja/Nein-Spalte ausblenden

Normalerweise kann man einzelne Spalten sehr einfach über die Einstellungen beim Inhaltstyp ausblenden. Diese Möglichkeit hat uns Microsoft (warum auch immer) bei Ja/Nein-Spalten leider genommen. In diesem Beitrag wird gezeigt, wie man das mit Hilfe von SharePoint Designer erreichen kann.

Wenn man bei einem Inhaltstyp auf einen Spaltennamen klickt, gelangt man zu einer Seite, auf der man u.a. angeben kann, ob diese Spalte erforderlich, optional oder ausgeblendet sein soll. Z.B. bei einer Standard-Titelspalte sieht das so aus:

Diese Einstellungen sind bei einer Ja/Nein-Spalte ausgegraut, so daß diese praktische Möglichkeit ein Feld zu verstecken hier nicht zur Verfügung steht. Weil ich gerade mal wieder darüber gestolpert bin, habe ich kurz Google bemüht. Eigentlich wollte ich herausfinden, warum das so ist. Das ist mir zwar auf die Schnelle nicht gelungen, aber dafür habe ich hier eine praktische Lösung gefunden. Ich halte das für eines der kuriosesten und am besten versteckten Features von SharePoint Designer.

Um das Gewünschte zu erreichen, hatte ich bisher entweder SharePoint Manager bemüht, mit dem man die Hidden-Eigenschaft einer solchen Spalte problemlos umsetzen kann oder die Spalte wurde ohnehin per Feature erzeugt und auch dort kann man sie problemlos ausblenden.

Lösung mit SharePoint Designer

Man öffnet die Website in SharePoint Designer und geht dann entweder über die Websiteinhaltstypen oder über die Liste und deren Inhaltstyp(en). In den Einstellungen zum Inhaltstyp, der eine auszublendende Spalte enthält, klickt man entweder links oben auf Spalten bearbeiten oder auf den Link in der Mitte Inhaltstypspalten bearbeiten. Man gelangt zu einer Liste mit allen Spalten des Inhaltstyps:

Wie kann man die Einstellung in der dritten Spalte jetzt ändern? Ganz einfach, wenn man es weiß: man klickt genau dreimal auf den Wert in der dritten Spalte und siehe da, sie verwandelt sich in ein DropDown und man kann den Wert ändern.

CAML-Abfragen bei wiederkehrenden Kalendereinträgen

Dieser Beitrag zeigt, wie man wiederkehrende Ereignisse in SharePoint-Kalendern über eine CAML-Abfrage per Code auslesen kann. Der Beitrag ist eine Ergänzung zu einem allgemeinen Beitrag über CAML, den ich hier geschrieben habe.

Wiederkehrende Ereignisse werden in einer Kalenderliste durch ein einziges Element repräsentiert. Man bekommt sie also nicht über eine Anfrage mit einer normalen Where-Klausel. Wenn man über eine Anfrage alle Ereignisse zu einem bestimmten Zeitpunkt oder zu einem bestimmten Zeitraum haben möchte, muß man eine bestimmte Syntax in CAML notieren. Das sieht dann so aus:

<Where>
  <DateRangesOverlap>
    <FieldRef Name=’EventDate’/>
    <FieldRef Name=’EndDate’/>
    <FieldRef Name=’RecurrenceID’/>
    <Value Type=’DateTime‘><Today/></Value>
  </DateRangesOverlap>
</Where>

Damit diese Anfrage funktioniert, muß die ExpandRecurrence-Eigenschaft des SPQuery-Objekts auf true festgelegt werden. Wenn man sonst nichts weiter angibt, bekommt man durch diese Abfrage alle Ereignisse für heute. Falls ein anderes Datum gewünscht ist, muß außerdem die CalendarDate-Eigenschaft des SPQuery-Objekts auf das gewünschte Datum festgelegt werden. Der gesamte Code sieht dann so aus:

SPQuery query = new SPQuery();
query.ExpandRecurrence = true;
query.CalendarDate = new DateTime(2012, 12, 4);
query.Query = @"<Where>
                  <DateRangesOverlap>
                    <FieldRef Name=’EventDate’/>
                    <FieldRef Name=’EndDate’/>
                    <FieldRef Name=’RecurrenceID’/>
                    <Value Type=’DateTime‘><Today/></Value>
                  </DateRangesOverlap>
                </Where>";

Falls man nicht nur die Ereignisse für einen einzelnen Tag möchte, kann man bei <Value> auch <Month/> oder <Year/> verwenden. Alle anderen Angaben bei <Value> funktionieren nicht. Insbesondere das abzufragende Datum muß bei SPQuery.CalendarDate angegeben werden!

Achtung:

Die durch eine solche Abfrage erhaltenen Elemente müssen nochmal geprüft werden, ob sie auch wirklich zum gewünschten Datum oder Zeitraum gehören. Man bekommt meist auch angrenzende Ereignisse geliefert. Bei einer Abfrage für einen Monat bekommt man z.B. auch die Ereignisse des Vormonats und des Folgemonats, die in einer Monatsansicht des Kalenders angezeigt werden würden. Bug oder Feature?

„Gefällt mir“ im SharePoint 2010 Blog

In diesem Beitrag wird gezeigt, wie man eine SharePoint 2010 Blogwebsite mit Facebook-ähnlicher Gefällt mir Funktionalität ausstatten kann. Die gezeigte Lösung ist mit JavaScript realisiert und kommt deshalb komplett ohne Postbacks aus. Das Ergebnis wird sich später so darstellen:

Wir beginnen, indem wir eine neue Website aus der Vorlage Blog erstellen.

Die Likes-Liste

Welcher Person welcher Beitrag gefällt, wird in einer SharePoint-Liste gespeichert. Wir legen also eine neue Liste an und geben ihr den Namen Likes. Man klickt dazu auf Websiteaktionen Weitere Optionen, markiert dann die Vorlage Benutzerdefinierte Liste und klickt rechts auf Weitere Optionen. Die Markierung bei Liste in der Schnellstartleiste anzeigen setzen wir auf Nein und klicken dann auf Erstellen.

Die Liste muß jetzt noch etwas verändert werden. Dazu klickt man im Reiter Liste auf Listeneinstellungen. Die Standardspalte Titel brauchen wir nicht. Die Spalte ist aber als Pflichtfeld gekennzeichnet und das müssen wir ändern. Dazu klickt man auf den Spaltennamen Titel und setzt die Markierung bei Diese Spalte muss Informationen enthalten auf Nein.

Um die einzelnen Likes einem Blogbeitrag zuzuordnen, brauchen wir jetzt noch eine Nachschlagespalte auf die Beiträge. Wir klicken dazu auf Spalte erstellen, geben der neuen Spalte den Namen Beitrag und wählen als Informationstyp Nachschlagen (in Informationen, die sich bereits auf dieser Website befinden). Nachdem wir geprüft haben, daß auch wirklich in der Liste Beiträge nachgeschlagen wird, klicken wir auf OK.

Weitere Anpassungen wie z.B. spezielle Ansichten sind nicht notwendig, da in dieser Liste niemand direkt Daten einträgt. Man könnte sie sogar komplett aus der Oberfläche ausblenden, indem man ihre Hidden-Eigenschaft auf true setzt (was nicht im Browser geht).

JavaScript

Den benötigten JavaScript-Code legt man am Besten in eine eigene .js-Datei und bindet diese dann ein. Es gibt viele Möglichkeiten, wo man diese Datei ablegen kann. Das kann irgendwo in der Website, z.B. in einer Assets-Bibliothek, aber auch im Dateisystem der Webfrontend-Server sein, z.B. in einem eigenen Unterordner von 14\TEMPLATE\LAYOUTS. Für die Demo hier erstellen wir eine einfache Dokumentbibliothek Assets, die nicht in der Schnellstartleiste angezeigt wird und bei der wir als Vorlage Keine wählen. Unser Script kommt in eine Datei Likes.js, die wir in diese Bibliothek hochladen.

Vorhandene Likes anzeigen

Wir fangen an mit einer Funktion, die alle derzeit vorhandenen Likes anzeigt. Der Funktion übergeben wir die ID des Blogbeitrags, für den die Likes angezeigt werden sollen:

function getAllPostLikes(postId) {
  // hier holen wir uns die Likes und stellen sie dar
}

In dieser Funktion holen wir uns zunächst den Kontext und die erforderlichen Objekte wie die Likes-Liste und den aktuellen Benutzer. Diesen brauchen wir um festzustellen, ob dem aktuellen Benutzer dieser Beitrag gefällt oder nicht:

var ctx = new SP.ClientContext();
var web = ctx.get_web();
var currentUser = web.get_currentUser();
ctx.load(currentUser);
var list = web.get_lists().getByTitle("Likes");

Die vorhandenen Likes zum Beitrag holen wir jetzt über eine kleine CAML-Abfrage aus der Liste:

var query = new SP.CamlQuery();
query.set_viewXml("<View><Query><Where><Eq><FieldRef Name=’Beitrag‘ LookupId=’True’/><Value Type=’Lookup‘>" + postId + "</Value></Eq></Where></Query></View>");
var items = list.getItems(query);
ctx.load(items);

Sämtliche Interaktionen mit dem Server müssen im SharePoint Client Object Model immer asynchron ausgeführt werden. Man gibt dabei jeweils eine Funktion an, die bei Erfolg und im Fehlerfall aufgerufen wird. Fehler ignorieren wir hier einfach und die Funktion für den Erfolg geben wir inline an. Das Gerüst dazu sieht also so aus:

ctx.executeQueryAsync(function () {
  // alle Likes stehen jetzt zur Verfügung
  }, function () { }
);

In dieser Funktion bauen wir uns den HTML-Quelltext zusammen, mit dem wir später die Anzahl der Likes darstellen. Auch den Button, mit dem der Benutzer später einen Beitrag "Liken" kann, bzw. einen "Like" zurücknehmen kann, erstellen wir dort als HTML.

Die Anzahl der Likes erstellen wir als einfachen Text und fügen davor und dahinter Leerraum ein, damit sich die Darstellung später besser in die Seite einpaßt. Man könnte hier z.B. noch eine Fallunterscheidung machen und für die Fälle, daß der Beitrag bisher niemandem oder nur einer Person gefällt, andere Texte anzeigen. Hier soll aber nur so einfach wie möglich das Prinzip gezeigt werden.

var html = "&nbsp;|&nbsp;" + items.get_count() + " Person(en) gef&auml;llt das&nbsp;&nbsp;";

Jetzt ermitteln wir, ob dem aktuellen Benutzer der Beitrag bereits gefallen hat. Dazu vergleichen wir einfach die ID desjenigen, der ein Like erstellt hat mit der ID des aktuellen Benutzers:

var userLikesThis = false;
for (var i = 0; i < items.get_count(); i++) {
  var item = items.get_item(i);
  var author = item.get_item("Author");
  if (author.get_lookupId() == currentUser.get_id()) {
    userLikesThis = true;
    break;
  }
}

Jetzt fehlt noch der Teil zur Darstellung der Like- bzw. Unlike-Buttons. Wir erzeugen hier einfache HTML-Buttons, die beim Klick entsprechende JavaScript-Funktionen aufrufen und dabei den Button selbst, die ID des Beitrags und die ID des aktuellen Benutzers übergeben. Diese Funktionen werden im Anschluß behandelt. Auch hier könnte man die Darstellung sicher noch aufhübschen, aber wie gesagt: es geht nur um das Prinzip:

if (userLikesThis) {
  html += "<input type=’button‘ value=’Gef&auml;llt mir nicht mehr‘ onclick=’unlikePost(this, " + postId + ", " + currentUser.get_id() + "); return false;’/>";
} else {
  html += "<input type=’button‘ value=’Gef&auml;llt mir‘ onclick=’likePost(this, " + postId + ", " + currentUser.get_id() + "); return false;’/>";
}

Wir haben jetzt das HTML zur Darstellung beieinander und fügen es jetzt in die Seite ein. Dazu setzen wir die innerHTML-Eigenschaft eines in der Seite platzierten <span>-Elements. Bei diesem Element wurde die id um die ID des Beitrags ergänzt, so daß wir jetzt gezielt darauf zugreifen können. Wie das Element erzeugt wurde, zeige ich weiter unten.

$get("PostLikesSpan" + postId).innerHTML = html;

Einen Beitrag "Liken"

Wenn ein Benutzer auf den Gefällt mir Button klickt, wird diese Funktion aufgerufen:

function likePost(btn, postId, userId) {
  btn.enabled = false;
}

In der Funktion wird zuerst der geklickte Button selbst deaktiviert, um zu verhindern, daß ein Benutzer für einen Beitrag mehrfach die Gefällt mir Funktion auslösen kann. Ähnlich wie oben holen wir uns jetzt ein paar Objekte und suchen dann nach einemeventuell bereits vorhandenen Like für den aktuellen Benutzer. Die CAML-Abfrage dazu ist ebenfalls ähnlich wie oben, aber weiter eingeschränkt auf den Benutzer:

query.set_viewXml("<View><Query><Where><And><Eq><FieldRef Name=’Beitrag‘ LookupId=’True’/><Value Type=’Lookup‘>" + postId + "</Value></Eq>" +
"<Eq><FieldRef Name=’Author‘ LookupId=’True’/><Value Type=’Lookup‘>" + userId + "</Value></Eq></And></Where></Query></View>");

Nach dem Ausführen der Abfrage prüfen wir, ob es bereits ein Like gibt und nur wenn nicht erzeugen wire in neues Element in der Likes-Liste. Danach rufen wir einfach die getAllPostLikes-Funktion von oben wieder auf, um die Anzeige zu aktualisieren:

if (items.get_count() == 0) {
  var itemCreateInfo = new SP.ListItemCreationInformation();
  var item = list.addItem(itemCreateInfo);
  item.set_item("Beitrag", postId);
  item.update();
  ctx.load(item);
  ctx.executeQueryAsync(function () {
    getAllPostLikes(postId);
  }, function () { }
  );
}

Einen Beitrag "Unliken"

Die Funktion zum Zurücknehmen eines Gefällt mir ist ähnlich aufgebaut. Der Unterschied besteht darin, daß ein gefundenes Element in der Likes-Liste gelöscht wird:

if (items.get_count() > 0) {
  var item = items.get_item(0);
  item.deleteObject();
  ctx.executeQueryAsync(function () {
    getAllPostLikes(postId);
  }, function () { }
  );
}

In die Seiten einbinden

In einer Blogwebsite werden die Beiträge auf verschiedenen Seiten dargestellt. Hier wird gezeigt, wie man die Gefällt mir Funktion in die Startseite default.aspx einbaut. Auf den anderen Seiten wie z.B. Category.aspx oder Date.aspx funktioniert das analog.

Die Darstellung der Blogbeiträge erfolgt immer durch ein XsltListViewWebPart und wir müssen dort eine Erweiterung im XSL einbauen und unsere Likes.js Datei referenzieren. Wir öffnen also die Website in SharePoint Designer, klicken links auf Alle Dateien und öffnen dann default.aspx im erweiterten Modus. Der erweiterte Modus ist notwendig, damit man Änderungen im Kopf der Seite machen kann.

Wir suchen den Platzhalter PlaceHolderAdditionalPageHead und fügen dort die Referenz auf unsere Scriptdatei ein:

<script type="text/javascript" src="/blog/Assets/Likes.js"></script>

Als nächstes müssen wir noch eine Ergänzung in die Ansicht der Beiträge einbauen. Damit man überhaupt an das XSL gelangt, markiert man das WebPart für die Beitragsanzeige, z.B. indem man einfach in der Entwurfsansicht draufklickt. Dann klickt man im Reiter Entwurf auf XSLT anpassen und dann auf Gesamte Ansicht anpassen. Dadurch wird das XSL in die aktuelle Seite kopiert und kann bearbeitet werden.

Man sucht sich jetzt eine geeignete Stelle für die Gefällt mir Funktion und markiert sie in der Entwurfsansicht. Wenn man jetzt in die Codeansicht umschaltet, ist der Teil im XSL markiert, der für die Darstellung des markierten Bereichs zuständig ist. Manchmal muß man das ein paar Mal machen, bis es wirklich klappt.

Wir wählen für die Demo die Stelle rechts neben der Anzahl der Kommentare. Man findet den Code dazu am einfachsten, wenn man das Wort Kommentar(e) markiert. Im Code sieht die Stelle so aus:

<xsl:value-of select="$thisNode/../@resource.wss.num_comments_blg_post"/>
</a>
</span>
</xsl:template>

Wir fügen unsere Erweiterung zwischen dem schließenden </span> und dem schließenden </xsl:template> ein:

<span id="PostLikesSpan{$thisNode/@ID}"> </span>
<script type="text/javascript">
  ExecuteOrDelayUntilScriptLoaded(getMyLikes<xsl:value-of select="$thisNode/@ID" />, &quot;sp.js&quot;);
  function getMyLikes<xsl:value-of select="$thisNode/@ID" /> () {
    getAllPostLikes(<xsl:value-of select="$thisNode/@ID" />);
  }
</script>

Damit wird zuerst das oben bereits erwähnte <span>-Element erzeugt, bei dem die id die eindeutige ID des Beitrags enthält. Anschließend wird eine JavaScript-Funktion erzeugt, die ebenfalls diese ID im Namen hat. Diese Funktion wird über ExecuteOrDelayUntilScriptLoaded aufgerufen. Dadurch wird dafür gesorgt, daß die für das Client Object Model notwendigen Scripte zur Verfügung stehen. Die aufgerufene Funktion ruft wiederum unsere getAllPostLikes-Funktion auf und übergibt die ID des Beitrags. Über ExecuteOrDelayUntilScriptLoaded kann man nur Funktionen ohne Parameter aufrufen, daher der Umweg.

Man muß jetzt noch den Beginn des <xsl:template> suchen und dort das ddwrt:ghost-Attribut entfernen. Zu diesem Thema habe ich hier etwas geschrieben.

Abschluß

Die hier ausschnittsweise gezeigte und erklärte JavaScript-Datei kann hier heruntergeladen werden. Der Download enthält auch eine deutsche Websitevorlage, aus der eine Blogwebsite mit eingebauter Gefällt mit Funktion erzeugt werden kann.

Die gezeigte Lösung bietet noch viel Raum für Erweiterungen. Man könnte z.B. auch die Kommentare mit der Gefällt mir Funktion versehen. Und man könnte nicht nur die Anzahl der Personen anzeigen, denen etwas gefällt, sondern auch wer diese Personen sind. Z.B. als Tooltip oder Popup, wenn man mit dem Mauszeiger darüber fährt. Das alles sollte sich durch erweitern der gezeigten Lösung relativ leicht machen lassen.

Benachrichtigungen verwalten

Dieser Beitrag zeigt wie ein Benutzer alle seine Benachrichtigungen, die er irgendwo in SharePoint angelegt hat, an einer zentralen Stelle verwalten kann.

Disclaimer: ich habe das nicht selbst herausgefunden. Der Beitrag geht auf das Mitglied petersen der SharePoint Community zurück. Sie hat die Lösung hier gepostet. Nochmals vielen Dank dafür.

Im Browser gibt es die Seite Meine Benachrichtigungen, die aber nur die eigenen Benachrichtigungen der aktuellen Website auflistet. Also noch nicht mal alle Benachrichtigungen der aktuellen Websitesammlung. Die Lösung für das Problem ist in Outlook eingebaut. Registerkarte Start – Menü Regeln Regeln und Benachrichtigungen verwalten – Registerkarte Benachrichtigungen verwalten. Dort können zentral alle eigenen Benachrichtigungen eingesehen, geändert und gelöscht werden. Auch neue Benachrichtigungen können angelegt werden.

Farben im Kalender

In diesem Beitrag wird gezeigt wie man ganz einfach verschiedene Einträge eines Kalenders in SharePoint 2010 mit verschiedenen Farben darstellen kann.

Zur Unterscheidung der Einträge, d.h. zur Unterscheidung welcher Eintrag in welcher Farbe dargestellt werden soll, verwenden wir das Feld Kategorie, das bei jedem Kalender standardmäßig dabei ist. Die Vorgehensweise funktioniert aber genauso mit jedem anderen Feld. Das Feld Kategorie ist ein Auswahlfeld, dessen Auswahleinträge beliebig angepaßt werden können, so daß auch eine andere Kategorisierung möglich ist. Wir verwenden hier die Standardauswahlen Feiertag und Geburtstag, d.h. wir wollen in einem Kalender Feiertage und Geburtstage gleichzeitig aber mit verschiedenen Farben darstellen.

Wir erstellen uns einige Demoeinträge. Für den Oktober 2012 kann das z.B. so aussehen:

Wie man sieht, lassen sich Feiertage und Geburtstage zunächst nur durch Lesen des Titels unterscheiden.

Eine Ansicht für jede Farbe

Um die unterschiedlichen Typen auseinanderzuhalten, erstellen wir zunächst für jede Kategorie eine eigene Ansicht, die nur die Einträge dieser Kategorie enthält. Dazu erstellt man einfach eine neue Ansicht abgeleitet von der Standardansicht Kalender und definiert dabei einen Filter für das Feld Kategorie:

Dasselbe macht man für die Kategorie Geburtstag.

Kalenderüberlagerung

Zur Darstellung der Einträge in verschiedenen Farben machen wir uns jetzt die Kalenderüberlagerung zunutze. Damit kann einer Kalenderansicht in SharePoint 2010 ein anderer Kalender überlagert werden und man kann angeben, in welcher Farbe der überlagerte Kalender angezeigt werden soll. Als Quelle für die Überlagerung wählen wir einfach die eben definierten Ansichten.

Damit die Einträge in der Standardansicht nicht doppelt erscheinen, also einmal durch die Standardansicht selbst und einmal durch die Überlagerung, ändern wir die Standardansicht einfach. Wir definieren dort einen Filter, der die durch Überlagerung angezeigten Einträge entfernt. In unserem Fall blenden wir einfach alle Einträge aus, bei denen das Feld Kategorie leer ist:

Um eine Kalenderüberlagerung zu definieren, geht man auf die Standardansicht des Kalenders und klickt im Reiter Kalender auf Kalenderüberlagerung:

Auf der folgenden Seite klickt man auf Neuer Kalender. Man vergibt einen Namen für die Überlagerung, läßt die Einstellung bei Kalendertyp bei SharePoint, wählt eine Farbe aus, gibt die URL zu einer vorher definierten Ansicht an und klickt dann auf Auflösen. Die beiden Dropdowns darunter werden dann automatisch mit den richtigen Werten gefüllt. Wichtig ist, daß ganz unten der Haken bei Immer anzeigen gesetzt wird, damit die Überlagerungen auch dauerhaft angezeigt werden. Zum Überlagern der Feiertage sieht das so aus:

Für die Geburtstage geht man analog vor.

Das war es auch schon. Im Kalender werden jetzt Einträge ohne Kategorie, Feiertage und Geburtstage mit unterschiedlichen Farben dargestellt:

Meta-Elemente in Veröffentlichungsseiten

In diesem Beitrag soll gezeigt werden, wie man in SharePoint 2010 Veröffentlichungsseiten Meta-Elemente pflegen und ausgeben kann. Der Beitrag soll als Dokumentation zu einem Vortrag bei der SharePoint Usergroup Stuttgart dienen. Dieser wiederum geht auf eine Anfrage von Dennis Scherrer zurück. Dennis hat im Vorfeld auch Recherchearbeit dazu geleistet, deshalb auch hier nochmal ein herzliches Danke dafür.

Was sind Meta-Elemente?

Hier der Link zur englischen Wikipedia-Seite. Eine kurze und prägnante Beschreibung gibt es hier bei Microsoft:

"Metatags sind für den Benutzer nicht sichtbare Codeabschnitte, die von Suchmaschinen häufig zum Indizieren von Websites verwendet werden. Anhand der Metatags können Suchmaschinen die relevantesten Ergebnisse für eine bestimmte Suchanfrage zurückgeben. Zwei wichtige Metatags sind das Tag keyword und das Tag description."

Microsoft spricht hier zwar von Tags, korrekter ist aber die Bezeichnung Elemente, da es sich um eigene Elemente innerhalb des HTML-Quelltextes handelt.

Meta-Elemente werden immer als leere Elemente mit name– und content-Attribut notiert. In der fertigen Seite erscheinen sie so:

<meta name="description" content="Das ist die Beschreibung der Seite" />

SharePoint Veröffentlichungsseiten

Meta-Elemente werden von der On-Premise-Version von SharePoint 2010 nicht direkt unterstützt. In SharePoint Online ist die Unterstützung allerdings eingebaut. Wie man sie benutzt, steht ebenfalls auf der oben verlinkten Seite.

Im Folgenden wird anhand eines Enterprise-Wiki gezeigt, wie man die Unterstützung für Meta-Elemente hinzufügt. Das Enterprise-Wiki wurde gewählt, weil es einfacher strukturiert und damit das Beispiel leichter nachzuvollziehen ist. Die Vorgehensweise läßt sich aber 1:1 auf die "normalen" Veröffentlichungsseiten übertragen.

Websitespalten

Wir brauchen zuerst die Felder, die die Informationen aufnehmen, die wir später als Meta-Elemente ausgeben wollen. Dazu legt man für jedes gewünschte Element eine Websitespalte vom Typ Mehrere Textzeilen an. Wichtig dabei ist, daß bei zulässigem Texttyp Nur-Text gewählt wird. Der Feldinhalt wird später als Attributwert im HTML verwendet und Rich-Text-Formatierungen sind dort nicht erlaubt (und damit sinnlos). Im Beispiel verwenden wir eine Spalte für description und eine für keywords:

Als nächstes müssen wir dafür sorgen, daß wir diese Spalten auch in unseren Wikiseiten verwenden können. Die Seiten im Enterprise-Wiki basieren standardmäßig auf dem Inhaltstyp Unternehmenswiki-Seite. Wir fügen die Spalten also diesem Inhaltstyp hinzu.

Meta-Inhalte pflegen

Man könnte die Daten jetzt pflegen, indem man auf die Seitenbibliothek geht, eine Seite auswählt und ihre Eigenschaften bearbeitet:

Das ist natürlich nicht das, was wir unseren Benutzern anbieten möchten. Die Benutzer wollen auch die Meta-Informationen so pflegen, wie sie die eigentlichen Inhalte der Seite pflegen. Ein Benutzer geht dazu auf die gewünschte Seite und klickt auf das Bearbeiten-Symbol oder den Link Diese Seite berabeiten, wählt Seite bearbeiten aus dem Menü Websiteaktionen oder geht auf den Reiter Seite und dort auf Seite bearbeiten. In jedem Fall wird dadurch die Seite in den Bearbeitungsmodus versetzt und die Inhalte können geändert werden. Allerdings erscheinen unsere Felder noch nicht im Bearbeitungsmodus und können damit nicht mit Inhalt gefüllt werden.

Das Aussehen und Verhalten aller Wikiseiten wird durch die Vorlage EnterpriseWikis.aspx festgelegt. Wir müssen also diese Vorlage bearbeiten, damit wir unsere Felder pflegen können. Dazu öffnet man die Website in SharePoint Designer. Am Einfachsten geht das über Websiteaktionen In SharePoint Designer bearbeiten. Die Vorlage findet man dann im linken Menü unter Seitenlayouts. Man klickt auf EnterpriseWiki.aspx und dann auf Datei bearbeiten. Übrigens findet man unter Seitenlayouts auch die ganzen anderen Vorlagen, die für Veröffentlichungsseiten benutzt werden und die man auf dieselbe Weise bearbeiten kann.

Wenn die Seite geladen ist, sucht man sich eine geeignete Stelle, an der man die Felder zur Pflege einbinden möchte. Im Beispiel wurde der Bereich rechts unterhalb Kategorien gewählt. Die eigentlichen Änderungen macht man dann in der Codeansicht.

Wir möchten unsere Felder nur im Bearbeitungsmodus darstellen. Die Inhalte selbst sollen ja unsichtbar als Meta-Elemente ausgegeben werden. Um etwas nur im Bearbeitungsmodus auszugeben, bietet SharePoint das praktische Control Microsoft.SharePoint.Publishing.WebControls.EditModePanel, das so eingefügt wird:

<PublishingWebControls:EditModePanel class="ewiki-margin" runat="server">
</PublishingWebControls:EditModePanel>

Dieses Control sorgt automatisch dafür, daß sein Inhalt nur im Bearbeitungsmodus in der Seite erscheint. Zur Pflege unserer Felder verwenden wir ein weiteres mitgeliefertes Control: Microsoft.SharePoint.WebControls.FormField. Es wird so eingefügt:

<SharePoint:FormField id="metaDescField" ControlMode="Edit" FieldName="MetaDescription" runat="server"/>

Wichtig sind die Attribute ControlMode, das unbedingt auf Edit gesetzt warden muß, und FieldName, das den internen Namen des Feldes enthalten muß. Als Wert für id kann ein beliebiger Wert verwendet werden.

Das Tagprefix SharePoint steht in der Wikivorlage standardmäßig nicht zur Verfügung. Man muß die notwendige Deklaration noch im Kopf der Seite einfügen:

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

Insgesamt fügen wir zur Pflege der Metainformationen also folgendes ein (hier wurde noch eine Überschrift verwendet):

<PublishingWebControls:EditModePanel class="ewiki-margin" runat="server">
 
<b>Meta-Informationen:</b><br/>
 
<SharePoint:FormField id="metaDescField" ControlMode="Edit" FieldName="MetaDescription" runat="server"/>
 
<SharePoint:FormField id="metaKeyField" ControlMode="Edit" FieldName="MetaKeywords" runat="server"/>
</PublishingWebControls:EditModePanel>

Damit haben wir bereits alles, was wir zur Pflege der Meta-Informationen benötigen. Eine Seite im Bearbetungsmodus sieht dann so aus:

Meta-Elemente ausgeben

Wir müssen jetzt noch dafür sorgen, daß unsere Felder im Kopf der Seite als Meta-Elemente ausgegeben werden. Den Code dazu fügt man in den Platzhalter PlaceHolderAdditionalPageHead ein.

Zum Ausgeben eines bestimmten Feldwertes gibt es ebenfalls ein praktisches Control: Microsoft.SharePoint.WebControls.FieldValue. Es wird so verwendet:

<SharePoint:FieldValue id="metaDescValue" FieldName="MetaDescription" runat="server"/>

Wichtig ist auch hier wieder, daß das Attribut FieldName mit dem internen Namen des gewünschten Feldes belegt wird.

Damit wird allerdings nur der eigentliche Feldinhalt im HTML-Quelltext ausgegeben. Wir brauchen diesen Inhalt aber im content-Attribut eines meta-Elements. Für Meta-Elemente gibt es kein eigenes Control in ASP.NET und wir müssen uns deshalb mit Literal-Controls behelfen. Mit einem Literal kann man direkt HTML-Text erzeugen. Wir verwenden also jeweils ein Literal um den Teil des meta-Elements vor und hinter dem eigentlichen Feldwert auszugeben. Es sind dann so aus:

<asp:Literal ID="metaDesc1" runat="server" Text="&lt;meta name=&quot;description&quot; content=&quot;"/>
<SharePoint:FieldValue id="metaDescValue" FieldName="MetaDescription" runat="server"/>
<asp:Literal ID="metaDesc2" runat="server" Text="&quot;&gt;"/>

Damit haben wir alles, um in einem Enterprise-Wiki Meta-Informatione zu pflegen und korrekt als meta-Elemente in der Seite auszugeben. Die genannten Anpassungen müssen allerdings in jeder Websitesammlung neu durchgeführt werden. Bei Veröffentlichungsseiten ist das etwas umfangreicher als beim Enterprise-Wiki, weil mehr Inhaltstypen und mehr Seitenvorlagen verwendet werden.

Fertige Lösung

Dennis ist bei seinen Recherchen auf eine kostenlose Lösung gestoßen, die den Prozeß etwas automatisiert, die Mavention Meta Fields. Dabei handelt es sich um eine SharePoint Farm-Solution. Alle Texte sind fest einprogrammiert englisch. Zur Verwendung muß das zugehörige Websitesammlungsfeature aktiviert werden. Dadurch wird ein Delegate Control registriert, das den Platzhalter PlaceHolderAdditionalPageHead ersetzt.

Dieses Control prüft, ob es sich bei der aktuellen Seite um eine Veröffentlichungsseite handelt und falls ja, werden alle Felder der Seite, die zur Gruppe Publishing Meta Tags (fest einprogrammiert) gehören, als meta-Elemente ausgegeben. Als name-Attribut wird dabei der sichtbare Name des Feldes verwendet.

Außerdem fügt das Control im Bearbeitungsmodus ein div-Element in die Seite ein (als Kind des form-Elements). Dieses Element enthält wiederum Controls, die als Pflegemaske für die Felder dienen. Das Element wird dabei durch fest einprogrammiertes CSS positioniert und mittels jQuery 1.4.2 aus- und eingeblendet.

Eigene Ribbon-Controls werden nicht angezeigt

Im Web gibt es genügend Beiträge, die zeigen, wie man eigene Controls in das SharePoint-Ribbon einfügt. Einige davon habe ich hier verlinkt. In diesem Beitrag soll auf eine mögliche Ursache hingewiesen werden, warum eigene Controls im Browser nicht erscheinen.

Diese Problematik hatte uns fast einen Tag gekostet, bis wir die Ursache mehr durch Zufall entdeckt haben. Betroffen sind eigene Ribbon-Controls, die im Ansichts-Ribbon einer Liste oder Bibliothek erscheinen sollen, also im Reiter "Elemente" bzw. "Dokumente". Dabei spielt es keine Rolle, ob die eigenen Controls deklarativ, also durch Definition in der elements.xml eines eigenen Features, oder durch Programmcode als SPUserCustomAction angelegt wurden.

Das Problem ist nicht offensichtlich. Das Standard-Ribbon der Liste bzw. Bibliothek erscheint und funktioniert, aber die eigenen Controls wollen einfach nicht erscheinen. Ursache dafür ist eine Einstellung der Ansicht, die in den Webparteinstellungen gesetzt werden kann (Websiteaktionen – Seite bearbeiten – Webpart bearbeiten):

Nur wenn die Einstellung für den Symbolleistentyp auf "Vollständige Symbolleiste" steht (englisch "Full Toolbar"), werden im Ribbon auch eigene Controls angezeigt. Bei Listen- und Bibliotheksansichten ist das zwar die Standardeinstellung, nicht aber bei den für jede Liste vorhandenen Ansichtswebparts. Außerdem hatten wir diese Einstellung auch bei den Standardansichten geändert, weil man damit auf einfache Weise den "Neues Element hinzufügen"-Link ausblenden kann.

Dialog bei Klick auf Lookup-Link abschalten

In SharePoint 2010 kann man bei Listen und Bibliotheken einstellen, ob die Listenformulare als Dialog geöffnet werden sollen oder nicht. Hyperlinks, die von Nachschlagefeldern (Lookups) erzeugt werden, beachten diese Einstellung aber nicht – sie werden immer als Dialog geöffnet. In diesem Beitrag soll gezeigt werden, wie man die Dialoge für diese Links abstellt.

Nachschlagefelder erzeugen bei der Ansicht immer einen Link auf das nachgeschlagene Element. Das gilt sowohl für das Standardanzeigeformular (DispForm.aspx) als auch für jede Listenansicht. Ein Klick auf einen solchen Link öffnet immer das Standardanzeigeformular des nachgeschlagenen Elements in einem Dialog. Dieser Dialog ist nicht immer erwünscht, z.B. wenn das Anzeigeformular selbst stark angepaßt wurde und deshalb möglichst viel Platz erhalten soll. Oder der Link soll sich gleich in einem neuen Fenster oder in einem neuen Reiter öffnen.

Um das zu erreichen, verwenden wir mal wieder jQuery. Wo man jQuery bekommt und wie man es einbindet, habe ich hier beschrieben.

Man geht dabei wie folgt vor: der Link eines Nachschlagefeldes wird bereits als normales <a>-Tag gerendert. Das Tag besitzt ein gültiges href-Attribut, das wir direkt verwenden können. Es besitzt aber auch ein onclick-Attribut, das für den Dialog verantwortlich ist. Wir müssen also nur die richtigen <a>-Elemente per JavaScript ausfindig machen und deren onclick-Attribut entfernen. Optional kann man dabei auch gleich ein target="_blank"-Attribut einfügen, falls sich die Links in einem neuen Fenster öffnen sollen.

Man öffnet dazu die Website in SharePoint Designer und navigiert zu der Liste oder Bibliothek, für die eine Ansicht angepaßt werden soll. Die Ansicht wird zum Bearbeiten geöffnet. In der Codeansicht sucht man das öffnende Element von PlaceHolderMain und fügt sein Script direkt darunter ein:

<asp:Content ID="Content1" ContentPlaceHolderId="PlaceHolderMain" runat="server">

Wenn man sich den Quelltext einer fertigen Seite im Browser anschaut, stellt man fest, daß die gesuchten Links alle mit der CSS-Klasse ms-vb2 versehen sind. Das machen wir uns zunutze, um die Links zu finden:

var allLinks = $(".ms-vb2 a");

Jetzt können wir die Links alle durchgehen und das onclick-Attribut entfernen:

allLinks.each(function() {
  $(this).removeAttr("onclick");
});

Das Ganze packen wir in eine handliche und wiederverwendbare Funktion:

function noDialogLinks() {
  var allLinks = $(".ms-vb2 a");
  allLinks.each(function() {
    $(this).removeAttr("onclick");
  });
}

Diese Funktion muß jetzt nur noch aufgerufen werden, sobald die Seite fertig geladen ist. Da Ansichten in SharePoint per Ajax geladen werden, müssen wir dabei eine kleine Verzögerung einbauen, damit sichergestellt ist, daß beim Ausführen unserer Funktion auch wirklich alle Daten zur Verfügung stehen:

$(document).ready(function() {
  setTimeout(noDialogLinks, 2000);
});

Hier wird eine Pause von 2.000 Millisekunden, also zwei Sekunden gemacht. In der Praxis sollte das ausreichen, damit die Daten geladen werden können und es sollte kurz genug sein, damit niemand vorher auf einen Link klickt.

Gruppierte Ansichten

Aufpassen muß man, wenn gruppierte Ansichten verwendet werden, die beim ersten Laden alle Knoten geschlossen/eingeklappt anzeigen. Hier werden die einzelnen Zeilen erst geladen, wenn ein Knoten geöffnet/ausgeklappt wird.

Damit unser Script in diesem Fall immer noch funktioniert, müssen wir alle Gruppierungsknoten ausfindig machen, an deren onclick-Funktion anhängen und dann ebenfalls wieder unsere noDialogLinks-Funktion aufrufen. Auch dabei müssen wir wieder eine kleine Verzögerung einbauen, weil die Daten erst geladen werden müssen. Der Code dazu sieht so aus:

var groupLinks = $(".ms-gb a");
groupLinks.each(function() {
  $(this).click(function() {setTimeout(noDialogLinks, 1500);} );
});

Hier nochmal zusammengefaßt das gesamte Script:

<script type="text/javascript">
function noDialogLinks() {
  var allLinks = $(".ms-vb2 a");
  allLinks.each(function() {
    $(this).removeAttr("onclick");
  });
}

$(document).ready(function () {
  setTimeout(noDialogLinks, 2000);
  var groupLinks = $(".ms-gb a");
  groupLinks.each(function() {
    $(this).click(function() { setTimeout(noDialogLinks, 1500); });
  });
});
</script>

Schnellstart-Link in neuem Fenster öffnen

In diesem Beitrag wird gezeigt, wie man einen Link in der Schnellstartleiste so manipulieren kann, daß er sich in einem neuen Fenster (oder in einem neuen Reiter) öffnet.

Man geht dabei wie folgt vor: der gewünschte Link muß per JavaScript/ECMAScript ausfindig gemacht und dann ein Attribut target="_blank" angehängt werden. Am Einfachsten geht das, wenn man jQuery verwendet.

Man braucht dazu die jQuery-Library, die hier heruntergeladen werden kann. Die Datei kann irgendwo in der Website, z.B. in der immer vorhandenen Formatbibliothek, abgelegt werden. Besser ist es aber sie direkt in einen passenden Ordner im Dateisystem der Webfrontendserver zu legen, weil man sie von dort in jeder beliebigen Website einbinden kann. Ich verwende dazu normalerweise einen eigenen Unterordner von 14\LAYOUTS.

Das Script muß in die Gestaltungsvorlage (Masterpage) eingebaut werden. Dazu öffnet man die Website in SharePoint Designer. Die Gestaltungsvorlage findet man (wer hätte es gedacht), wenn man links auf "Gestaltungsvorlagen" klickt. Die Standard-Masterpage heißt V4.master. Die Seite öffnet man in der Codeansicht und sucht das Ende des <head>-Abschnitts, d.h. man macht die Ergänzungen vor der schließenden Zeile </head>.

Zunächst wird die jQuery-Library referenziert:

/_layouts/Demo/jquery-1.6.1.min.js

jQuery macht es sehr einfach bestimmte Elemente anzusprechen. In diesem Fall suchen wir einen Link, also ein <a>-Element, der auf eine bestimmte URL verweist:

$("a[href=’/subsite/‘]")

Bei dieser Methode muß immer der gesamte Link angegeben werden (man beachte, daß Links in der Schnellstartleiste meist Server-relativ sind). Es gibt aber noch andere Möglichkeiten, als den direkten "ist gleich"-Vergleich:

= bedeutet "exakt gleich"
!= bedeutet "ungleich"
^= bedeutet "beginnt mit"
$= bedeutet "endet mit"
*= bedeutet "enthält"

Dem Link wird jetzt das Attribut target="_blank" angehängt:

.attr("target", "_blank")

Das gesamte Script sieht dann so aus:

/_layouts/Demo/jquery-1.6.1.min.js

  $(document).ready(function() {
    $(„a[href=’/subsite/‘]“).attr(„target“, „_blank“);
  });

Per EventReceiver in einer Dokumentbibliothek auf das Feld Titel zugreifen

Wenn man in einem synchronen EventReceiver (-ing) einer Dokumentbibliothek auf die Standard-Titelspalte (Title) zugreifen möchte, wird man beim ersten Versuch sehr wahrscheinlich eine Überraschung erleben. Man bekommt weder einen neu eingegebenen Wert, noch kann man dieses Feld ändern. Die Lösung für dieses Problem ist ganz einfach (wenn man es weiß): man muß dort das Feld "vti_title" verwenden.

Der Effekt tritt nur in synchronen Ereignissen wie ItemAdding oder ItemUpdating und nur in Dokumentbibliotheken auf. Normalerweise kann man bei diesen Ereignissen über

properties.AfterProperties["InternerFeldname"]

auf die neuen Werte zugreifen. Ebenso kann man die Werte setzen, indem man den gewünschten Wert als string in die AfterProperties schreibt:

properties.AfterProperties["InternerFeldname"] = "Neuer Wert"

Wenn man das mit dem Standardfeld Titel versucht, dessen interner Name immer Title ist, wird man feststellen, daß man falsche Werte erhält und daß Änderungen nicht übernommen werden. Lösung: bei den genannten Umständen (synchrones Ereignis einer Dokumentbibliothek) muß als Feldname immer "vti_title" verwendet werden.

Falls jemand eine Begründung für dieses mehr als seltsame Verhalten kennt, möge er mir diese bitte mitteilen. Würde mich brennend interessieren.