Link zum Erstellen eines Detaildatensatzes

Wenn man zwei 1:n-verbundene Listen hat, wird oft ein Link gewünscht, über den man einen Detaildatensatz erstellen kann und bei dem das Nachschlagefeld zum zugehörigen Elterndatensatz bereits vorbelegt ist. Hier wird gezeigt wie man das mit Hilfe von etwas JavaScript umsetzen kann.

Das grundsätzliche Vorgehen dabei ist folgendes: man erstellt einen eigenen Link, mit dem ausgehenden von einem Elternelement ein neues Detailelement erstellt werden kann. Der Link verweist dabei auf das Formular zur Neuanlage der Detailliste und enthält als zusätzlichen Parameter die ID des Elternelements. In das Formular zur Neuanlage wird JavaScript eingebaut, das diesen Parameter wieder aus der URL abholt und das Nachschlagefeld damit vorbelegt. Falls gewünscht kann man das Nachschlagefeld dann auch ausblenden.

Ausgangslage

Es gibt eine Parentliste, die hier zu Demozwecken ganz einfach aufgebaut ist und nur die Titelspalte enthält:

Dazu gibt es eine Detailliste, die ebenso einfach aufgebaut ist und außer dem Titel nur ein Nachschlagefeld auf die Parentliste enthält:

Man hat dadurch, ähnlich wie bei einem klassischen relationalen Datenmodell, eine 1:n-Verbindung zwischen den Listen. Jedem Elterndatensatz können mehrere Kinddatensätze zugeordnet sein. Jeder Kinddatensatz ist genau einem Elterndatensatz zugeordnet. In der Praxis wird so etwas z.B. bei Rechnungen (ein Datensatz Rechnungskopf mit Rechnungsnummer, Anschrift usw. und mehrere Datensätze Rechnungspositionen mit Artikelnummer, Preis usw.) oder bei Kontaktdaten (ein Datensatz Firma mit Kundennummer, Anschrift usw. und mehrere Datensätze Ansprechpartner mit Name, Telefonnummer usw.) verwendet.

Darstellung

Um die Daten jetzt immer schön im Zusammenhang darzustellen, kann man sehr einfach zu jedem Elterndatensatz die zugehörigen Kinddatensätze anzeigen lassen. Man fügt dazu einfach eine Ansicht der Detailliste auf das Anzeige- und das Ändern-Formular (DispForm und EditForm) der Elternliste ein. SharePoint sorgt dafür, daß diese Ansicht korrekt gefiltert wird.

Zum Einfügen geht man so vor: man geht auf die Elternliste, klickt auf Liste, öffnet den Menüpunkt Formularwebparts ändern und wählt das anzupassende Formular aus:

 

Die Menüpunkte der einzelnen Formulare sind meiner Meinung nach etwas unglücklich übersetzt. Neues Standardformular meint das Formular zur Neuanlage (NewForm.aspx), Standardanzeigeformular meint das Formular zur Anzeige (DispForm.aspx) und Standardformular bearbeiten meint das Formular zum Ändern (EditForm.aspx).

Wenn das gewünschte Formular geöffnet ist, klickt man irgendwo in die Seite, damit der Reiter Einfügen sichtbar wird. Dort gibt es dann einen Menüpunkt Verwandte Liste, der beim Aufklappen alle Listen anzeigt, die ein Nachschlagefeld auf die aktuelle Liste enthalten:

 

Man wählt die einzufügende Liste aus und es wird eine automatisch gefilterte Ansicht dieser Liste eingefügt. Die Ansicht kann jetzt wie gewohnt über die Webparteinstellungen angepaßt werden (Menüpunkt Webpart bearbeiten). Für uns ist hier wichtig den standardmäßig unter der Ansicht angezeigten Link Neues Element hinzufügen zu entfernen, damit wir ihn durch einen eigenen Link ersetzen können. Damit der Link nicht mehr angezeigt wird, setzt man in den Webparteinstellungen den Symbolleistentyp auf Keine Symbolleiste:

Der Neues Element erstellen Link

Um den ausgeblendeten Link durch einen eigenen zu ersetzen, gibt es zwei Möglichkeiten: man kann das Formular in SharePoint Designer öffnen und die Änderungen dort direkt in der Codeansicht machen. Oder man fügt im Browser noch ein Inhalts-Editor-Webpart ein und bearbeitet das in der HTML-Ansicht. Dieser Weg wird hier gezeigt.

Das Webpart findet sich in der Kategorie Medien und Inhalt und man fügt es am Sinnvollsten direkt unter der neu eingefügten Ansicht ein. Eventuell muß man es dazu im Browser nach unten verschieben. Nachdem man in das Webpart geklickt hat, wählt man im Reiter Text formatieren aus dem Menü HTML den Punkt HTML-Quelle bearbeiten:

Wir brauchen zunächst eine Funktion, die uns einen bestimmten Parameter aus der URL liefert:

function getQueryStringParameter(paramName) {

  var params = document.URL.split("?")[1].split("&"),

      i,

      singleParam;

  for (i = 0; i < params.length; i++) {

    singleParam = params[i].split("=");

    if (singleParam[0] == paramName)

      return singleParam[1];

  }

  return "";

}

Diese Funktion benutzen wir, um die ID aus der URL zu holen:

var lookupId = getQueryStringParameter("ID");

Damit setzen wir uns die URL zusammen, über die wir das Formular zur Neuanlage eines Kindelements aufrufen:

var newFormUrl = "/site/Lists/ChildList/NewForm.aspx?LookupId=" + lookupId;

Wenn man möchte, daß der Benutzer nach dem Speichern des neuen Elements (oder nach einem Klick auf Abbrechen) wieder auf die ursprüngliche Seite zurückgeleitet wird, kann man die dafür in SharePoint vorgesehene Standardtechnik benutzen und einen zusätzlichen Source-Parameter mitgeben. Der Parameter muß die Adresse der ursprünglichen Seite und zusätzlich wieder die ID als Parameter enthalten. Man kann das durch folgende Zeile erreichen:

newFormUrl += "&Source=" + encodeURI(window.location.pathname + "?ID=" + lookupId);

Diese URL benutzen wir, um einen vorher definierten Anker mit dem korrekten Link zu versorgen. Außerdem packen wir das Ganze in eine Funktion, damit man es nach dem vollständigen Laden der Seite aufrufen kann:

function createNewLink() {

  var lookupId = getQueryStringParameter("ID"),

      newFormUrl = "/site/Lists/ChildList/NewForm.aspx?LookupId=" + lookupId,

      link = document.getElementById("myNewLink");

  newFormUrl += "&Source=" + encodeURI(window.location.pathname + "?ID=" + lookupId);

  link.href = newFormUrl;

}

Achtung: dieses Verfahren funktioniert nur, wenn das aktuelle Formular nicht in einem Dialog geöffnet wurde. Dialoge kann man in den Listeneinstellungen unter Erweiterte Einstellungen abschalten. Eleganter ist es natürlich, wenn es auch von einem Dialog aus funktioniert und wenn das Formular zum Erstellen des neuen Elements ebenfalls in einem Dialog geöffnet wird.

Das zu erreichen ist aber relativ einfach: man gibt dem Anker einfach ein zusätzliches onclick-Attribut und ruft darin die von SharePoint vorgesehene Funktion auf:

link.onclick = function (event) { NewItem2(event, newFormUrl); return false; };

Dabei muß man der NewItem2-Funktion allerdings die absolute URL übergeben, die man so erzeugen kann:

newFormUrl = window.location.protocol + "//" + window.location.hostname + newFormUrl;

Hier nochmal zusammengefaßt der gesamte Code zum Kopieren und Einfügen in die HTML-Quelle des Webparts. Nicht vergessen, die newFormUrl auf die eigene Umgebung anzupassen!

<a href="#" id="myNewLink">Neues Element erstellen</a>

<script type="text/javascript">

_spBodyOnLoadFunctionNames.push("createNewLink");

function createNewLink() {

  var lookupId = getQueryStringParameter("ID"),

      newFormUrl = "/site/Lists/ChildList/NewForm.aspx?LookupId=" + lookupId,

      link = document.getElementById("myNewLink");

  link.href = newFormUrl;

  newFormUrl = window.location.protocol + "//" + window.location.hostname + newFormUrl;

  link.onclick = function (event) { NewItem2(event, newFormUrl); return false; };

}

function getQueryStringParameter(paramName) {

  var params = document.URL.split("?")[1].split("&"),

      i,

      singleParam;

  for (i = 0; i < params.length; i++) {

    singleParam = params[i].split("=");

    if (singleParam[0] == paramName)

      return singleParam[1];

  }

  return "";

}

</script>

NewForm anpassen / Nachschlagefeld vorbelegen

Mit dem oben Gezeigten haben wir jetzt also einen Link auf das Formular, mit dem die neuen Kindelemente erzeugt werden. Die ID des Elternelements wird dabei in der URL übertragen. Kommen wir jetzt also zu den Anpassungen, die man dort noch machen muß, um diese ID auszulesen und das Nachschlagefeld damit vorzubelegen.

Wie man den notwendigen JavaScript-Code auf ein Listenformular bekommt, habe ich ja oben schon beschrieben und werde deshalb hier nicht weiter darauf eingehen.

Um an die ID des Elternelements zu kommen, verwenden wir wieder die oben gezeigte Funktion getQueryStringParameter. Spätestens jetzt sollte man sich überlegen, diese Funktion in eine eigene js-Datei zu packen, damit man sie einfach wiederverwenden kann.

Außerdem brauchen wir eine Hilfsfunktion getTagFromIdentifierAndTitle, die tausendfach durchs Web geistert. Ich verzichte hier auf eine Quellenangabe, da ich ohnehin nicht weiß, wer sie ursprünglich erdacht hat. Sie liefert ein HTML-Element anhand des Tagnamens, einem optionalen Feldtyp und dem Feldnamen:

function getTagFromIdentifierAndTitle(tagName, identifier, title) {

  var idLength = identifier.length,

      tags = document.getElementsByTagName(tagName),

      i,

      tagID;

  for (i = 0; i < tags.length; i++) {

    tagID = tags[i].id;

    if (tags[i].title == title && (identifier == "" || tagID.indexOf(identifier) == tagID.length – idLength)) {

      return tags[i];

    }

  }

  return null;

}

Diese Funktion können wir jetzt benutzen, um an das Nachschlagefeld zu gelangen. Hier kommt aber eine besondere Verhaltensweise von Nachschlagefeldern hinzu, der besondere Beachtung geschenkt werden muß. Wenn die Nachschlageliste weniger als 20 Elemente enthält, wird das Nachschlagefeld als ganz normales <select> gerendert. Man muß dann einfach nur das gesuchte <option> anhand seines Values finden und auswählen. Dazu dient diese Funktion:

function setSelectedOption(select, value) {

  var opts = select.options,

      optLength = opts.length,

      i;

  for (i = 0; i < optLength; i++) {

    if (opts[i].value == value) {

      select.selectedIndex = i;

      return true;

    }

  }

  return false;

}

Wenn die Nachschlageliste mehr Elemente enthält, wird für das Nachschlagefeld ein komplizierteres DHTML-Konstrukt gerendert. Das Haupteingabefeld ist dabei ein <input type="text">. Der gespeicherte Wert befindet sich allerdings in einem <input type="hidden"> und dieses wiederum findet man über seine ID, die beim Eingabefeld in einem optHid-Attribut gespeichert ist. Puh.

Da das Feld später vorbelegt und sinnvollerweise von den Benutzern nicht geändert werden soll, blenden wir es aus. Wir wollen dabei nicht nur das Feld selbst ausblenden, sondern gleich die gesamte Tabellenzeile, in der es sich befindet. Auch dabei müssen die beiden Arten unterschieden werden. Beim <select> geht es drei Ebenen nach oben, bis man die Tabellenzeile erreicht. Beim <input> sind es vier Ebenen.

Achtung: man darf die Felder nicht auf disabled oder ähnliches setzen, weil sonst ihre Werte beim POST nicht übertragen und damit nicht gespeichert werden!

Damit können wir uns jetzt eine wiederverwendbare Funktion bauen, die den Wert eines Nachschlagefeldes unabhängig von der Anzahl der Nachschlageelemente setzt und auch gleich das Feld ausblendet:

function setLookupField(fieldName, value) {

  var theSelect = getTagFromIdentifierAndTitle("select", "", fieldName),

      theInput;

  if (theSelect == null) {

    theInput = getTagFromIdentifierAndTitle("input", "", fieldName);

    document.getElementById(theInput.optHid).value = value;

    theInput.parentNode.parentNode.parentNode.parentNode.style.display = "none";

  } else {

    setSelectedOption(theSelect, value);

    theSelect.parentNode.parentNode.parentNode.style.display = "none";

  }

}

Auch hier nochmal der gesamte JavaScript-Code für die NewForm zum bequemen Kopieren und Einfügen. Nicht vergessen, den Namen des Nachschlagefelds (hier "Parent") an die eigene Umgebung anzupassen! 

<script type="text/javascript">

_spBodyOnLoadFunctionNames.push("setLookupFromQS");

function setLookupFromQS() {

  var lookupId = getQueryStringParameter("LookupId");

  setLookupField("Parent", lookupId);

}

function setLookupField(fieldName, value) {

  var theSelect = getTagFromIdentifierAndTitle("select", "", fieldName),

      theInput;

  if (theSelect == null) {

    theInput = getTagFromIdentifierAndTitle("input", "", fieldName);

    document.getElementById(theInput.optHid).value = value;

    theInput.parentNode.parentNode.parentNode.parentNode.style.display = "none";

  } else {

    setSelectedOption(theSelect, value);

    theSelect.parentNode.parentNode.parentNode.style.display = "none";

 }

}

function getTagFromIdentifierAndTitle(tagName, identifier, title) {

  var idLength = identifier.length,

      tags = document.getElementsByTagName(tagName),

      i,

      tagID;

  for (i = 0; i < tags.length; i++) {

    tagID = tags[i].id;

    if (tags[i].title == title && (identifier == "" || tagID.indexOf(identifier) == tagID.length – idLength)) {

      return tags[i];

    }

  }

  return null;

}

function setSelectedOption(select, value) {

  var opts = select.options,

      optLength = opts.length,

      i;

  for (i = 0; i < optLength; i++) {

    if (opts[i].value == value) {

      select.selectedIndex = i;

      return true;

    }

  }

  return false;

}

function getQueryStringParameter(paramName) {

  var params = document.URL.split("?")[1].split("&"),

      i,

      singleParam;

  for (i = 0; i < params.length; i++) {

    singleParam = params[i].split("=");

    if (singleParam[0] == paramName)

      return singleParam[1];

  }

  return "";

}

</script>

MachineKeys in der web.config von SharePoint

Wenn man SharePoint mit FBA (Formularbasierter Authentifizierung / Forms Based Authentication) arbeitet, braucht man in vielen Fällen einen sogenannten MachineKey (Computerschlüssel) wenn man Benutzerpasswörter nicht im Klartext ablegen möchte. Sobald man eine Webanwendung für FBA konfiguriert, erzeugt SharePoint einen solchen Schlüssel und trägt ihn in die web.config der Webanwendung ein.

Wenn man mehrere Webanwendungen gegen dieselbe Benutzerdatenbank fahren möchte oder auch nach einer Migration, ist es notwendig hier einzugreifen und eigene Schlüssel zu verwenden. In diesem Fall wird man sich wahrscheinlich sehr schnell wundern, weil sich am nächsten Tag kein Benutzer mehr anmelden kann. Verantwortlich dafür ist ein Timerjob, der regelmäßig jede Nacht ausgeführt wird und der den ursprünglichen Schlüssel wieder in die web.config einträgt.

Man sollte jetzt aber nicht einfach den gesamten Timerjob deaktivieren, weil dieser mehrere Integritätsanalysen (Health Analysis) ausführt. Allerdings kann man die verantwortliche Regel der Integritätsanalyse deaktivieren. Für das Zurücksetzen des Computerschlüssels ist die Regel ViewStateKeysAreOutOfSync zuständig und man kann sie mit folgenden PowerShell-Befehlen deaktivieren:

Get-SPHealthAnalysisRule ViewStateKeysAreOutOfSync | Disable-SPHealthAnalysisRule

Link zum Erstellen eines neuen Dokuments aus einer Vorlage

Und weil ich gerade dabei bin oft gestellte Fragen zu beantworten, gleich noch ein derartiger Beitrag. Diesmal geht es darum einen Hyperlink darzustellen, mit dem ein neues Dokument aus einer Vorlage erzeugt werden kann.

Ansichten von Dokumentbibliotheken enthalten unterhalb der Dokumente standardmäßig einen Link "Dokument hinzufügen", der allerdings nur auf das Upload-Formular verweist. Wenn man wirklich ein neues Dokument aus einer hinterlegten Vorlage erstellen möchte, muß man zuerst den Reiter Dokumente aktivieren, das Menü Neues Dokument öffnen und dann den gewünschten Inhaltstyp auswählen. Um das zu vereinfachen, wäre es schön, wenn man an beliebiger Stelle einen Link platzieren könnte, der dieselbe Aufgabe erfüllt, und genau das wird hier gezeigt.

Es gibt grundsätzlich viele Möglichkeiten, wie man irgendwo in SharePoint einen Link einfügen kann. Das kann in einer Wikiseite oder in einem Inhalts-Editor-Webpart auf einer Webpartseite sein. Mit Hilfe von SharePoint Designer kann man Links an fast beliebigen Stellen einbauen. In jedem Fall braucht es JavaScript, damit die Office-Integration auch wirklich funktioniert. Der Link selbst sieht so aus:

<a href="#" onclick="XYZ; return false;">Neues Dokument</a>

Das eigentliche Verweisziel (href-Attribut) läßt man leer bzw. es enthält nur eine Raute #. Ins onclick-Attribut kommt an der Stelle von XYZ die eigentliche JavaScript-Funktion (gleich mehr dazu) und ein return false. Neues Dokument ist der angezeigte Text des Links.

Die Funktion, die in den Link statt XYZ eingesetzt wird, sieht grundsätzlich so aus:

CoreInvoke(‚createNewDocumentWithProgIDEx‘, event, ‚Url der Vorlage‘, ‚Url der Bibliothek‘, ‚SharePoint.OpenDocuments‘, false);

Bei Url der Vorlage gibt man die absolute URL der gewünschten Vorlage an. Man findet sie in der Regel im Ordner Forms der Bibliothek und dort in einem Ordner mit dem Namen des Inhaltstyps. Bei Url der Bibliothek gibt man die absolute URL der Zielbibliothek an. Sie wird benötigt, damit Office die Bibliothek als Standardort beim Speichern des neuen Dokuments vorschlägt. Achtung: beide URLs müssen wirklich absolut sein. Relative funktionieren nicht!

Der gesamte Link kann also so aussehen:

<a href="#" onclick="CoreInvoke(‚createNewDocumentWithProgIDEx‘, event, ‚http://sharepoint/website/DocLib/Forms/Word1/WordTemplate1.dotx&#8216;, ‚http://sharepoint/website/DocLib&#8216;, ‚SharePoint.OpenDocuments‘, false); return false;">Neues Dokument</a>

Personenfelder vorbelegen mit jQuery

Vor einigen Tagen hatte ich hier einen Beitrag geschrieben, der zeigt, wie man Personenfelder mit JavaScript vorbelegen kann. Dort wurden die standardmäßig vorhandenen Möglichkeiten verwendet. In diesem Beitrag wird gezeigt, wie man das Ganze mit Hilfe von jQuery und damit deutlich einfacher erledigen kann. Außerdem hat das den Vorteil, daß man die gewünschten Personenfelder über ihren internen Namen ansprechen kann (anstatt über ihren Index wie im anderen Beitrag).

Ich verwende hier jQuery 1.6.1, ganz einfach weil es ohnehin bereits auf dem Server vorhanden war. Jede neuere (und wahrscheinlich auch ältere) Version sollte aber ebenfalls funktionieren. Ich habe die jQuery-Bibliothek hier aus dem Dateisystem, d.h. aus einem Unterordner von 14\TEMPLATE\LAYOUTS eingebunden, aber man kann sie auch innerhalb einer Website z.B. in die Formatbibliothek hochladen und von dort verlinken.

Die grundsätzliche Vorgehensweise ist hier dieselbe, wie beim vorigen Beitrag: man ermittelt den aktuellen Benutzer per Client Object Model und schreibt nach einem executeQueryAsync dessen Namen in das gewünschte Feld. Ich gehe hier also nur darauf ein, wie man mit jQuery auf ein Personenfeld zugreift.

Vor dem eigentlichen Script muß zunächst jQuery eingebunden werden:

<script type="text/javascript" src="/_layouts/Demo/jquery-1.6.1.min.js"></script>

Nachdem der gewünschte Benutzer erfolgreich geladen wurde, definieren wir zunächst einen regulären Ausdruck, der zur Identifizierung des gewünschten Personenfeldes verwendet wird:

var searchRegEx = RegExp("Fieldname=\"PeopleField\"", "gi");

Dann benutzen wir jQuery um damit alle Tabellenzellen der rechten Seite eines Formulars durchzugehen. Wir verwenden dazu die dort verwendete CSS-Klasse:

$("td.ms-formbody").each(function () {
  // $(this) entspricht hier einer Tabellenzelle
  // eines Listenformulars
});

Innerhalb der Tabellenzelle suchen wir jetzt nach dem vorher definierten regulären Ausdruck:

if (searchRegEx.test($(this).html())) {
  // wir haben die richtige Zelle gefunden
}

Jetzt können wir relativ einfach auf die Bestandteile des Personenfeldes zugreifen, also auf das <div> für die Standardeingabe und das <textarea> für ältere Browser, und sie mit dem Namen des Benutzers belegen:

$("div[Title=’Personenauswahl‘]", this).html(currentUser.get_title());
$("textarea[Title=’Personenauswahl‘]", this).val(currentUser.get_title());

Wichtig: die Elemente warden über ihr title-Attribut identifiziert, das bei Personenfeldern immer dieselben Werte enthält. Allerdings unterscheiden sich die Werte je nach Sprache. Bei einer englischen Website steht dort "People Picker" statt "Personenauswahl".

Damit steht der Name schon mal im Personenfeld. Wenn man den Namen auch gleich validieren möchte, so daß er im Feld wie gewohnt unterstrichen dargestellt wird, kann man einfach noch einen Klick auf den entsprechenden Button simulieren:

$("img[Title=’Namen überprüfen‘]", this).trigger("click");

Wichtig: auch hier wird wieder je nach Sprache unterschieden. Englisch steht dort "Check names" statt "Namen überprüfen".

Auch hier nochmal der gesamte JavaScript-Code zum bequemen Kopieren&Einfügen:

<script type="text/javascript" src="/_layouts/Demo/jquery-1.6.1.min.js"></script>
<script type="text/javascript">
ExecuteOrDelayUntilScriptLoaded(setPicker, "sp.js");
function setPicker() {
  var ctx = SP.ClientContext.get_current(),
      web = ctx.get_web(),
      currentUser = web.get_currentUser(),
      fieldName = "PeopleField",
      searchRegEx = RegExp("Fieldname=\"" + fieldName + "\"", "gi");

  ctx.load(currentUser);
  ctx.executeQueryAsync(function () {
      $("td.ms-formbody").each(function () {
        if (searchRegEx.test($(this).html())) {
          $("div[Title=’Personenauswahl‘]", this).html(currentUser.get_title());
          $("textarea[Title=’Personenauswahl‘]", this).val(currentUser.get_title());
          $("img[Title=’Namen überprüfen‘]", this).trigger("click");
          return;
        }
      });
    }, function (sender, args) {
      alert("Fehler: " + args.get_message());
    }
  );
}
</script>

Personenfelder vorbelegen

Eine oft gestellte Frage zu SharePoint ist die, wie man ein Personenfeld mit dem aktuellen Benutzer vorbelegen kann und genau das soll in diesem Beitrag gezeigt werden.

Es gibt grundsätzlich zwei Möglichkeiten: die erste besteht darin, das Feld erst nach der eigentlichen Neuanlage zu setzen. Das kann dann z.B. per EventReceiver oder per Workflow geschehen. Die zweite Möglichkeit besteht darin, das Eingabefeld auf dem Formular zur Neuanlage (NewForm.aspx) per JavaScript vorzubelegen und genau das wird hier gezeigt.

Update: hier ein ergänzender Beitrag, der zeigt wie man dasselbe deutlich einfacher mit jQuery erreichen kann. 

Wir brauchen also zunächst eine Möglichkeit das Formular mit JavaScript zu erweitern. Auch hierzu gibt es wieder zwei Möglichkeiten: die erste ist NewForm.aspx in SharePoint Designer zu öffnen und den Code direkt in die Seite einzufügen. Die zweite Möglichkeit ist das Formular im Browser zu bearbeiten, ein Inhaltseditor-Webpart einzufügen und dort den Code zu platzieren. Ich werde hier die Vorgehensweise mit SharePoint Designer beschreiben. Im Browser muß nur der fertige JavaScript-Code vom Ende des Beitrags in die HTML-Ansicht eines Inhaltseditor-Webparts kopiert werden.

Man öffnet also die Website in SharePoint Designer, klickt dann links auf Listen und Bibliotheken, dann auf die gewünschte Liste und schließlich rechts unter Formulare auf NewForm.aspx. Anschließend muß über das Menüband noch der erweiterte Modus aktiviert werden. In der Codeansicht sucht man sich jetzt eine geeignete Stelle zum Einfügen des Scripts. Geeignet sind z.B. die Platzhalter PlaceHolderAdditionalPageHead und PlaceHolderMain.

Zur Ermittlung des aktuellen Benutzers bedienen wir uns des Client Object Model für ECMAScript. Man könnte damit auch anderen Code ausführen, um einen bestimmten Benutzer zu ermitteln, aber für das Beispiel beschränke ich mich auf den aktuell angemeldeten Benutzer. Wenn man immer nur einen ganz bestimmten Benutzer vorbelegen möchte, kann man auch ganz darauf verzichten und diesen fest einprogrammieren.

Den aktuellen Benutzer bekommt man so:

var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var currentUser = web.get_currentUser();
ctx.load(currentUser);

Wie immer, wenn man das Client Object Model verwendet, muß anschließend ein executeQueryAsync ausgeführt werden, damit die gewünschten Daten auch wirklich geladen werden:

ctx.executeQueryAsync(function () {
   
// hier stehen die Daten zur Verfügung
  }, function (sender, args) {
    alert("Fehler: " + args.get_message());
  }
);

Die erste angegebene Funktion wird im Erfolgsfall und die zweite im Fehlerfall aufgerufen. Zum Testen kann man sich wie hier gezeigt eine Fehlermeldung ausgeben lassen. Bei einem Produktivsystem kann man die Funktion aber auch leer lassen und Fehler damit einfach "verschlucken".

Machen wir also an der Stelle weiter, die ausgeführt wird, wenn der Benutzer erfolgreich geladen wurde. Wir brauchen jetzt Zugriff auf das HTML-Element, das bei einem Personenfeld für die Eingabe vorhanden ist. Leider ist das bei Personenfeldern nicht ganz so einfach, wie bei anderen Feldtypen. Und es gibt nicht nur das altbekannte Eingabefeld (ein <div>), sondern auch ein standardmäßig ausgeblendetes <textarea>, das für ältere Browser erzeugt wird.

Da man Personenfelder im HTML nicht einfach über den Feldnamen identifizieren kann, erstellen wir eine wiederverwendbare Funktion, die alle Personenfelder durchgeht und das mit dem gewünschten Index zurückgibt. Wenn es mehrere Personenfelder gibt, können wir diese Funktion also mehrmals aufrufen und so z.B. das erste und das dritte Personenfeld vorbelegen:

function getPickerDiv(pickerNo) {
 
var divs = document.getElementsByTagName("DIV"),
      length = divs.length,
      i,
      j = 0;

  for
(i = 0; i < length; i++) {
    if (divs[i].id.indexOf("UserField_upLevelDiv") > 0) {
      if (j == pickerNo) {
        return divs[i];
      }
      j++;
    }
  }
  return null;
}

Diese Funktion liefert uns die <div>s für die Standardeingabemethode. Wir erstellen gleich noch eine zweite Funktion, die uns die <textarea>s liefert, damit wird die ebenfalls vorbelegen können:

function getPickerTextarea(pickerNo) {
 
var tas = document.getElementsByTagName("TEXTAREA"),
      length = tas.length,
      i,
      j = 0;

  for
(i = 0; i < length; i++) {
    if (tas[i].id.indexOf("UserField_downlevelTextBox") > 0) {
      if (j == pickerNo) {
        return tas[i];
      }
      j++;
    }
  }
  return null;
}

Jetzt brauchen wir diese Funktionen nur noch in den Code von oben einbauen und können dann den Benutzernamen z.B. in das erste Personenfeld einfügen:

var ele = getPickerDiv(0);
if (ele != null) {
  ele.innerHTML = currentUser.get_title();
}
ele = getPickerTextarea(0);
if (ele != null) {
  ele.value = currentUser.get_title();
}

Bitte beachten, daß beim <div> die innerHTML-Eigenschaft und beim <textarea> die value-Eigenschaft gesetzt wird.

Den Code packen wir jetzt noch in eine weitere Funktion setPicker, die dann beim Laden der Seite aufgerufen werden muß. Um dabei sicherzustellen, daß das Client Object Model zur Verfügung steht, verwenden wir die vordefinierte ExecuteOrDelayUntilScriptLoaded-Methode:

ExecuteOrDelayUntilScriptLoaded(setPicker, "sp.js");

Hier nochmal der gesamte JavaScript-Code zum bequemen Kopieren&Einfügen:

<script type="text/javascript">
ExecuteOrDelayUntilScriptLoaded(setPicker, "sp.js");
function setPicker() {
  var ctx = SP.ClientContext.get_current(),
      web = ctx.get_web(),
      currentUser = web.get_currentUser(),
      pickerNo = 0;

  ctx.load(currentUser);
  ctx.executeQueryAsync(function () {
      var ele = getPickerDiv(pickerNo);
      if (ele != null) {
        ele.innerHTML = currentUser.get_title();
      }
      ele = getPickerTextarea(pickerNo);
      if (ele != null) {
        ele.value = currentUser.get_title();
      }
    }, function (sender, args) {
      alert("Fehler: " + args.get_message());
    }
  );
}
function getPickerDiv(pickerNo) {
  var divs = document.getElementsByTagName("DIV"),
      length = divs.length,
      i,
      j = 0;

  for
(i = 0; i < length; i++) {
    if (divs[i].id.indexOf("UserField_upLevelDiv") > 0) {
      if (j == pickerNo) {
        return divs[i];
      }
      j++;
    }
  }
  return null;
}
function getPickerTextarea(pickerNo) {
  var tas = document.getElementsByTagName("TEXTAREA"),
      length = tas.length,
      i,
      j = 0;

  for
(i = 0; i < length; i++) {
    if (tas[i].id.indexOf("UserField_downlevelTextBox") > 0) {
      if (j == pickerNo) {
        return tas[i];
      }
      j++;
    }
  }
  return null;
}
</script>