SharePoint 2013: „Append Changes“ Textfelder auflösen

„Append Changes“-Felder sind praktisch

Textfelder, die mit dem „Append Changes“ Attribut ausgestattet sind, sind prinzipiell eine feine Sache. Jeder Eintrag wird mit Datum und eintragendem Benutzer zu dem bereits bestehenden Text hinzugefügt, der alte Eintrag ist nicht veränderbar. Großartig für Kommentarfelder oder jegliche Form von Journalen.

…aber ungünstig in der Listendarstellung.

In der Listenansicht wird so ein Feld jedoch nur als „View Entries“-Link auf die Leseansicht des Listeneintrags dargestellt. Unschön und vor allem fürchterlich unpraktisch. Das liegt vor allem daran, dass die Änderungen als Versionen gespeichert werden. Um alle Einträge zu bekommen müssen also alle Versionen des betreffenden Feldes gelesen und dargestellt werden. SharePoint liefert solch ein Control leider nur für die View- und Edit-Ansicht eines Items, nicht jedoch für die Listen-Ansicht.

Die Lösung

Doch es gibt Abhilfe. Seit der Version 2013 von SharePoint existiert die Möglichkeit per JavaScript in die Darstellung einzelner Felder in der Listenansicht einzugreifen. JSLink. Das ist nichts weiter als der Link zu einem JavaScript-File die dem ListView Webpart oder dem List-Schema.xml mitgegeben werden kann. In diesem Script lassen sich dann callback-Funktionen definieren für einzelne Felder definieren, die den Inhalt der Spalte liefern. Dieser Inhalt kann alles sein, was der Browser verarbeitet, meistens HTML und Javascript.

Aus diesem Grunde habe ich ein paar sinnvolle JavaScript Funktionen geschrieben, die man für sein JSLink-Script verwenden kann. Es handelt sich im Prinzip um zwei Funktionen, die die Änderungshistorie verschieden darstellen:

Als Popup, welches aufklappt wenn man mit der Maus darüberfährt:

bild1

Oder direkt in der betreffenden Zelle.

Der Code

Hier nun erstmal der Code:


// ------------------------------------------------------------------------------------------
// Shows the content of the current "append only" text field in a popup
function ShowVersionedPopup(ctx) {
    var listUrl = ctx.listUrlDir;
    var listName = ctx.ListTitle;

    var id = "FB_Mo_" + ctx.CurrentFieldSchema.ID + ctx.CurrentItem["ID"];
    html = "<img id='IMG" + id + "' src='/_layouts/15/images/myproject/moretext.png' onMouseOver=\"$('#" + id            + "').css('visibility','visible')\" onMouseOut=\"$('#" + id + "').css('visibility','hidden')\" />"
           + "
<div id='" + id + "' style='visibility:hidden; position:absolute; padding:5px;"            + "box-shadow: 10px 10px 29px 0px rgba(184,184,184,1); "            + "border: 2px solid #000000;"            + "background-color: white'"            + "></div>
";
    GetVersions(listName, ctx.CurrentItem["ID"], ctx.CurrentFieldSchema.Name, id);
    return html;
}

// ------------------------------------------------------------------------------------------
// Shows the content of the current "append only" text field directly in the list field
function ShowVersionedDiv(ctx) {
    var listUrl = ctx.listUrlDir;
    var listName = ctx.ListTitle;

    var id = "FB_Mo_" + ctx.CurrentFieldSchema.ID + ctx.CurrentItem["ID"];
    html = "
<div id='" + id + "'></div>
";
    GetVersions(listName, ctx.CurrentItem["ID"], ctx.CurrentFieldSchema.Name, id);
    return html;
}
// ------------------------------------------------------------------------------------------
// Retreives all versions of a field of a specifiv List Item
// listName: Name or ID of the List
// itemID: ID of the ListItem
// internalFieldName: The internal Name of the field
// elementId: The ID of the HTML element (usually a div) where the HTML output should be pumped in
function GetVersions(listName, itemId, internalFieldName, elementId)
{
    var xmlData = "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' "     xmlData += "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>";
    xmlData += "<soap:Body><GetVersionCollection xmlns='http://schemas.microsoft.com/sharepoint/soap/'>";
    xmlData += "<strlistID>" + listName + "</strlistID><strlistItemID>" + itemId + "</strlistItemID><strFieldName>"
    xmlData += internalFieldName + "</strFieldName></GetVersionCollection></soap:Body></soap:Envelope>";

    $.ajax({
        url: "<pfad zum web>/_vti_bin/lists.asmx", // <---- Hier Anpassen
        type: "POST",
        dataType: "xml",
        data: xmlData,
        complete: function (jqXHR) { GetVersionsSuccess(jqXHR, elementId, internalFieldName); },
        error: GetVersionsError,
        contentType: "text/xml; charset=\"utf-8\""
    });

};

// ------------------------------------------------------------------------------------------
// Callback func in terms of successful webService operation
// result: the result SOAP envelope
// divId: The ID of the HTML element (usually a div) where the HTML output should be pumped in
// internalFieldName: The internal Name of the SPList field
function GetVersionsSuccess(result, elementId, internalFieldName) {
    var rTable = "
<table>";
    $()
    //alert(result.esponseText);r
    if ($(result.responseXML).find("Version").length > 0) {
        $(result.responseXML).find("Version").each(function () {
            if ($(this).attr(internalFieldName) != "
<div></div>
") {
                var vDate = $(this).attr("Modified");
                rTable += "
<tr>
<td style='vertical-align: top; padding-bottom: 10px; padding-right: 8px;'>" + $(this).attr("Editor").split("#,#")[1] + " ("
                       + vDate.substr(5, 2) + "/" + vDate.substr(8, 2) + "/" + vDate.substr(0, 4)
                       + ")</td>
<td style='min-width:200px; vertical-align: top; padding-bottom: 10px;'>"
                       + lineEncode($(this).attr(internalFieldName)) + "</td>
</tr>
";
            }
        });
        rTable += "</table>
";
        $('#' + elementId).append(rTable);
    }
    else {
        // No entries, hide the icon (if there is one)
        // it is assumed that the corresponding image has the id IMG+divId
        if ($('#IMG' + elementId).length > 0) {
            $('#IMG' + elementId).css("visibility", "hidden");
        }
    }
}

// ------------------------------------------------------------------------------------------
// This fuction is called when an error in the request itself occurs
// (like 404 or timeout) - not when the webService returns an error
function GetVersionsError(result) {
    // just log it
    console.log(result);
}

function lineEncode(value) {
    //create a in-memory div, set it's inner text(which jQuery automatically encodes)
    //then grab the encoded contents back out.  The div never exists on the page.
    return value.replace('\n', '
').replace('\r','
');
}

Wie funktioniert es?

Die Versionen eines Feldes bekommt man mit dem SOAP Webservice lists.asmx und dessen Methode GetVersionsCollection. Dieser Webservice wird in der Funktion GetVersions() asynchron aufgerufen, das Ergebnis als HTML aufbereitet und per jQuery in das Containerelement (in diesem Fall ein <div>) hineingeschrieben.

Wie wird es benutzt?

Der obige Code ist wie gesagt Bestandteil des Scriptes, das per JSLink referenziert wird. An dem Script müssen folgende Dinge nach eigenen Bedürfnissen angepasst werden:

  • Der Pfad zum Icon (Zeile 8)
  • Der Pfad zum Web (Zeile 43)

Vor dem obigen Scriptblock muss noch der Aufruf erfolgen, bzw die Definition welche Felder durch welche Funktion dargestellt werden. Der folgende Block steht am Anfang des Script-Files der Block oben kommt einfach dahinter.

(function () {
    var fbListContext = {};
    fbListContext.Templates = {};

    fbListContext.Templates.Fields = {

        'KommentarAlsPopup' : {          // Interner Feldname
            'View': ShowVersionedPopup   // Als Popup darstellen
        },

        'KommentarInZelle': {            // Interner Feldname
            'View': ShowVersionedDiv     // Direkt in die Zelle schreiben
        }
    };

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(fbListContext);
})();

Dieser Codeblock steht am Anfang des JSLink Scriptes und definiert, welche Felder (interne Feldnamen!) durch welche Funktion dargestellt werden sollen. Die Funktonen ShowVersionedPopup und ShowVersionedDiv sind in dem ersten Codeblock definiert.

Danach muss noch das gesamte Script eingebunden werden, dafür gibt es verschiedene Möglichkeiten, die sich unter [1] nachlesen lassen.

Viel Spaß!

Weitere Infos

[1] JSLink Tutorial

Advertisements