Label in Confluence hinzufügen und löschen

Label an einer Seite im Confluence Wiki (Version 5.7.4) hinzuzufügen und zu löschen ist schwieriger als man denkt. Mit ein paar Tricks und intensiver Internet-Recherche bekommt man es aber doch hin. Hier zeige ich, wie es geht. Für die Lösung braucht man neben Confluence:

  • Benutzer-Makros
  • grundlegende HTML-Kenntnisse
  • JavaScript-Kenntnisse
  • die REST-Schnittstelle von Confluence

Heute kommen wir zu einem eher praktischen Problem, das ausnahmsweise nicht mit agilem Projektmanagement oder dem Faktor Mensch zu tun hat. Bei einem Kunden betreue ich ein DevOps-Team, das für den Betrieb eines großen Confluence Wiki zuständig ist. In einzelnen Bereichen des Wiki ist viel automatisiert. Einer der Bereiche realisiert einen Workflow über die Tags an den Seiten. Um den Workflow umzusetzen, werden an einer Seite je nach Status einzelne Label gesetzt oder entfernt. So wird einer Seite über die Label “offen”, “behoben”und “archiv” der jeweilige Zustand zugewiesen. Über einen Link auf der Seite kann der Zustand der Seite durch entfernen und hinzufügen gesetzt werden.

Aktion in Abhängigkeit von gesetztem Label

Der Link für den Zustandswechsel wird über ein Benutzer-Makro “Zustandwechsel” in der Seite angezeigt. Das Makro fügt je nachdem, welche Label an der Seite gesetzt sind ein weiteres Makro in die Seite ein (“aendere_offen_nach_behoben” oder “aendere_behoben_nach_archiv”).

Fangen wir mit dem Benutzer-Makro für die Unterscheidung an. Das Ausgabeformat muss auf “Wiki-Markup” gesetzt sein.


 

## Zustandswechsel-Link abhängig von Label einfügen
## @param required=true
#if ($content.getLabels().size() > 0)
  #foreach ($label in $content.getLabels())
    #if ($label.getName() == "behoben")
      {aendere_offen_nach_behoben}
    #elseif ($label.getName() == "archiv")
      {aendere_behoben_nach_archiv}
    #end
  #end
#end

 

Label hinzufügen und löschen

Jetzt müssen wir noch die Benutzer-Makros für das Anpassen der Label erstellen. Hier war ich erst etwas Blauäugig. Confluence stellt in allen Seiten ein JavaScript-Objekt zur Verfügung, dass neben jQuery-Funktionalität noch eine Reihe von Erweiterungen für Confluence mitbringt. Unter anderem gibt es dort das Objekt Label, dass Funktionen zum setzen und entfernen von Labeln  enthält:

AJS.Labels.addLabel( labelName, AJS.params.pageId, AJS.params.contentType);
AJS.Labels.removeLabel( labelID, AJS.params.pageId, AJS.params.contentType);

Leider war mir erst nicht bewusst, dass man für das Löschen die ID und nicht die Bezeichnung des Label braucht. Diese ID ist nicht so einfach zu beschaffen. Aber dazu später mehr. Zusätzlich muss die Seite nach dem Anpassen der Label neu geladen werden. Leider arbeiten die beiden oben genannten Methoden asynchron und kehren sofort zurück. Bei meinen ersten Versuchen wurde deswegen die Seite neu geladen, bevor der Request für das Hinzufügen und das Entfernen der Label abgearbeitet war. Deswegen wurde die neue Seite ohne sichtbare Änderung an den Labeln neu dargestellt. Die Ursache habe ich leider erst nach einigem Herumprobieren gefunden. Das ich zusätzlich versucht habe Label unter Angabe ihres Bezeichners zu entfernen, waren die Ergebnisse sehr frustrierend. Da unser Wiki sehr groß ist und die Ladezeiten nicht immer optimal und berechenbar sind, kam eine einfache Verzögerung vor dem Reload der Seite auch nicht in Frage. Deswegen musste ich am Ende auf “manuelle” POST-Anfragen über die REST-Schnittstelle ausweichen. Um unnötiges Wiederholen des dazu notwendigen JavaScript-Codes zu vermeiden, habe ich diesen im Benutzermakro “Zustandswechsel” angefügt. Wie ich zur ID gekommen bin, erkläre ich weiter unten. Der Code ist sicher nicht optimal, vermittelt aber eine Vorstellung vom grundsätzlichen Vorgehen. Eine mögliche Verbesserung wäre z.B., die jeweils nach erfolgtem Call aufzurufende Funktion, als Parameter an d er Funktion zu übergeben.


 

<script type="text/javascript">
function labelId(labelString) {
  var $labelLink = $('a.aui-label-split-main[href$="/label/its/'+labelString+'"]');
    if (!$labelLink.length) {
      window.location.reload();
    return;
  }
  return $labelLink.closest('.aui-label').attr('data-label-id');
}
function removeLabel(labelString) {
  var labelIdString = labelId(labelString)
  AJS.safe.ajax({
    type: "POST",
    url: AJS.params.contextPath + "/json/removelabelactivity.action",
    data: { entityIdString: AJS.params.pageId, labelIdString: labelIdString },
    success: function(response) {
      window.location.reload();
    },
    error: AJS.Labels.addLabelErrorHandler,
    dataType: "json"
  });
};
function addLabel(labelString, labelStringRemove) {
  AJS.safe.ajax({
    type: "POST",
    url: AJS.params.contextPath + "/json/addlabelactivity.action",
    data: { entityIdString: AJS.params.pageId, labelString: labelString },
    success: function(response) {
      removeLabel(labelStringRemove);
    },
    error: AJS.Labels.addLabelErrorHandler,
    dataType: "json"
  });
};
  
function addRemoveLabel(labelToAdd, labelToRemove) {
  addLabel(labelToAdd, labelToRemove);
}
</script>

 

Wie komme ich an die ID eines Label?

Wie ich oben bereits erwähnt habe, muss man für das Entfernen eines Labels die ID übergeben. Ich scheine nicht der Einzige zu sein, dem dies nicht bewusst war. Das Internet ist voll von Anfragen zu dem Thema, bei denen die Aufrufe mit dem Bezeichner erfolgen und auch nach langen Diskussionen alle Beteiligten rätseln, warum es nicht funktioniert. Erschwerend kommt hinzu, dass ich keine Möglichkeit gefunden habe die ID zentral abzufragen. Was nicht heißt, dass diese Möglichkeit nicht existiert. Deswegen bin ich einen anderen Weg gegangen.

In einer Wiki Seite, sind unten die Label als kleine Schaltflächen aufgeführt und wenn man die nötigen Rechte besitzt, kann ein Label durch einen Klick auf die Schaltfläche bearbeitet werden. Es besteht also die berechtigte Hoffnung, dass dort alle relevanten Informationen in Form von Attributen vorhanden sind.

Schaut man sich den zugehörigen HTML-Code im Entwicklungswerkzeug des bevorzugten Browsers an, findet man den unten stehenden Code. Zur besseren Lesbarkeit habe ich ihn etwas aufgeräumt und Teile weggelassen (markiert mit „…“).

<div id="likes-and-labels-container">
  <div id="likes-section"> … </div>
  <div id="labels-section" class="pageSection group">
    <div class="labels-section-content content-column" entityid="509452496" entitytype="page">
      <div class="labels-content">
        <ul class="label-list label-list-right  has-pen">
          <li class="aui-label " data-label-id="20906057">
            <a class="aui-label-split-main" href="/label/its/offen" rel="tag">offen</a>
          </li>
…

Relevante Bereiche habe ich oben hervorgehoben. Am <li> Tag findet man die ID des Label im Attribut data-label-id. Der Bezeichner des Label ist eine Ebene tiefer am Link im Namen und der URL zu finden. Den Code, um über den Namen an die ID zu kommen finden sie im letzten Kapitel in der Funktion labelId(). Der Aufbau des HTML in der Seite variiert bei Confluence leider stark von Version zu Version. Hier muss man eventuell Anpassungen vornehmen, wenn man mit einer anderen Confluence-Version als 5.7.4 arbeitet.

Die Makros für den Link

Jetzt fehlen eigentlich nur noch die Makros “aendere_offen_nach_behoben” und “aendere_behoben_nach_archiv”. Da beide Makros sich stark ähneln (bis auf den angezeigten Link-Text und die betroffenen Label, werde ich hier nur das Makro “aendere_offen_nach_behoben” aufführen. Die Transferleistung für das andere Makro dürfen sie selbst erbringen.

## Status von offen auf behoben aendern
## @noparams
<a href="#" onclick="addRemoveLabel('behoben', 'offen');">
Status auf 'behoben' setzen
</a>

Ich hoffe, dass ich ihnen damit weiter helfen konnte, falls sie ebenfalls mit Labeln in Confluence zu kämpfen hatten. Auf jeden Fall würde ich mich über ein Feedback in Form eines Kommentars freuen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.