HaloScan Kommentare via AJAX direkt auf der Blogseite anzeigen

HaloScan Kommentare direkt auf der Blogseite anzeigen

Ausgangssituation

Der Seitentyp "Blog" hat in der Version 3.5 von RapidWeaver große große Verbesserungen erfahren. Trotzdem gibt es noch einiges zu verbessern. Mit dem Erscheien des Plugins RapidBlog, hatte ich schon all meine Wünsche erfüllt gesehen. Aber leider geht das Plugin in eine andere Richtung. Mir kommt es nicht so sehr auf die Möglichkeit an, auch von Unterwegs Blogeinträge erstellen zu können, sondern mehr um einer bessere Integration des Kommentarsystems. Ich will nicht von meiner Seite wechseln um Kommentare zu lesen oder zu erstellen. Einer der beiden Punkte ist im RapidBlog Plugin bereits gelöst. Die Kommentare werden direkt auf der Blogseite angezeigt. Doch beim Schreiben von Kommentaren sieht es mit der Blogspot-Seite irgendwie noch schlimmer aus. Ich möchte also weiterhin die Blogfunktionalität von RapidWeaver nutzen, aber trotzdem die Komentare direkt auf meiner Seite sehen.

Ziel

Die Kommentare zu einer RapidWeaver Blog, sollen direkt auf den Blogseiten angezeigt werden. Die Lösung soll in das Theme der Seite eingebettet werden und möglicht einfach für auf den Betroffenen Seiten aktiviert werden können.

Lösung

Einlesen des Kommentarfeed

Als Quelle für die Kommentare dient uns der Kommentarfeed, der von HaloScan bereitgestellt wird. Die XML Form des Feeds bietet die optimale Grundlage zur weiterverarbeitung der Daten. Mit Hilfe eines AJAX Requests können wir den Feed im Hintergrund ahbolen und mittels JavaScript weiterverarbeiten. Hier stoßen wir aber auch schon auf das erste kleine Problem. Aus Sicherheitsgründen kann man mittels AJAX keine URL's aufrufen, die Außerhalb der aktuellen Domain liegen. Um dieses Problem zu lösen, schreiben wir uns zunächst ein kleines Proxy-Script z.B. in PHP. Da das Script nur den Feed von der entfernten URL abrufen und durchreichen muss, ist die Programmierung kein großes Problem.

<?php
  header("Content-type: text/xml");
  readfile("http://www.haloscan.com/members/rss.php?user=" . $_GET["user"]);
?>

Derzeit habe ich mich dafür entschieden das Proxy-Script als Seitenanlage (Asset) an die Blogseite zu hängen (In den "Seiten-Informationen" unter "Erweitert"). Ich habe aber schon mehrmals überlegt das Proxy-Script fest in der Theme-Datei unterzubringen.

Kommentar-Feed per AJAX abholen

Mit Hilfe des Proxy-Scripts können wir jetzt den Kommentar-Feed per AJAX-Request abholen. Zum Einsatz kommt dabei wieder die Prototype Library in der Version 1.5, die neben der AJAX-Unterstützung auch noch zahlreiche Funktionen mitbringt, die wir beim Umgang mit dem HTML Elementen gut gebrauchen können. Um die Prototype Library zu verwenden, muss diese natürlich in das Theme eingefügt werden. Hierzu sind folgende Dinge notwendig:

  • Hinzufügend der "prototype.js" zur Theme-Datei
  • Einbinden der Datei im Header der index.html des Themes
  • Angeben der Datei im "RWCopyFiles"
Die genaue forgehensweise kann in der "Theme Development" Dokumentation von RapidWeaver, bzw. habe ich im "RapidWeaver - Lightbox" Tutorial bereits beschrieben.

Das Script zum Abholen des Fedds ist auch noch sehr übersichtlich. Anhand der Existenz von HTML Elementen mit der Klasse "blog-archive-entries-wrapper", ob es sich bei der aktuellen Seite um die Hauptseite des Blogs handelt, oder um eine der Archiv-/Detailseiten. Abhängig davon wird der relative Pfad zum Proxy-Script gesetzt. Naschließend wird der AJAX-Request erzeugt. Als Parameter erhält dieser die Funktion, die im Erfolgsfall aufgerufen werden soll.

function getHaloComments() {
  var content = $('content');
  // Der Pfad zum Proxy-Script ist abhaengig davon, ob wir auf der Hauptseite des Blogs sind, 
  //   oder z.B. auf einer Archiv Seite. Nur auf Archivseiten existieren Container mit der 
  //   Klasse 'blog-archive-entries-wrapper'.
  var blogEntries = content.getElementsByClassName('blog-archive-entries-wrapper');
  var requestUrl;
  if (blogEntries.length > 0) {
    requestUrl = '../assets/haloscan.php';
  }
  else {
    requestUrl = 'assets/haloscan.php';
  };
  // Abrufen des Kommentar-Feeds mittels AJAX Request.
  var haloAjax = new Ajax.Request(requestUrl,
                                  {
                                    method: 'get',
                                    parameters: 'user=niedral',
                                    onComplete: addHaloComments,
                                    onFailure: function(){ alert('Something went wrong...')}
                                  });
}

Zuordnen und Einfügen der Kommentare

Jetzt kommt der schwierigste Teil. Die eingelesene XML Datei muss abgearbeitet werden, und aus den relevanten Informationen müssen die Kommentare generiert und eingefügt werden. Vom AJAX-Request bekommen wir ein DOM-Objekt der eingelesenen XML-Datei übergeben, auf das wir mit den entsprechenden JavaScript bzw. Prototype Funktionen zugreifen können. Auf diese Weise übertragen wir zunächst alle Kommentare in ein Hilfsarray/-objekt, wobei nur die Kommentare übertragen werden, die zur aktuellen Blogseite gehören. Die ID der aktuellen Blogseite, anhand derer wir die Kommentare ausfoltern, wird später als globale Variable definiert. Mit dem Hilfsarray/-objekt simuliere wir ein assoziatives Array, in dem die Kommentare nach Blogeinträgen zusammengefasst abgelegt werden. Da es bei JavaScript eigentlich keine assoziativen Array gibt, werden diese mit Hilfe von Objekten simuliert, so wie es im Artikel "Assoziative Arrays" auf SELFHTML beschrieben ist. Wahlweise könnte man an dieser Stelle auch das Hash Objekt der Prototype Library verwenden, das ebenfalls ein assoziatives Array nachbildet.
Nachdem die Kommentare herausgefiltert wurden suchen wir die Blogeinträge der Seite heraus und ordnen diesen die Kommentare zu. Für jeden Kommentar werden die benötigten HTML Objekte erzeugt und an den Blogeintrag angehängt.

function addHaloComments(originalRequest) {
       // Kommentare aus dem XML Response heraushohlen.
       var xmlResponse = originalRequest.responseXML;
    var items = xmlResponse.getElementsByTagName('item');
       // Alle Kommentare überprüfen und gegebenenfalls zwischenspeichern.
       var feedComments = new Object();
       var commentTitle, commentLink, commentDescription;
       var commentPageId, commentId;
       for (i = 0; i < items.length; i++) {
               for (j = 0; j < items[i].childNodes.length; j++) {
                       var node = items[i].childNodes[j];
                       switch (node.nodeName) {
                               case "title":
                                       commentTitle = node.firstChild.nodeValue;
                                       break;
                               case "link":
                                       commentLink = document.createTextNode(node.firstChild.nodeValue);
                                       break;
                               case "description":
                                       commentDescription = document.createTextNode(node.firstChild.nodeValue);
                                       break;
                       };
               };
               // Mittels Regulaerem Audruck die ID's der Seite und des Blogeintrages, sowie den Namen
               //   des Posters aus dem Titel des Kommentars extrahieren.
               var expression2 = /^Thread: rw unique entry id (\d+) (page\d+)\. Post by (.+)$/;
               expression2.exec(commentTitle);
               commentId = RegExp.$1;
               commentPageId = RegExp.$2;
               // Gehoert der Kommentar zur aktuellen Seite?
               if (commentPageId == pageId) {
                       // Falls nicht vorhanden, ein Array fuer die Kommentare des aktuellen Blogeintrags anlegen.
                       if (!feedComments[commentId]) {
                               feedComments[commentId] = new Array();
                       };
                       // Aktuellen Kommentar in einem Hilfobjekt speichern und das Objekt speichern.
                       var currComment = new Object();
                       currComment["title"] = "Kommentar von " + RegExp.$3;
                       currComment["description"] = commentDescription; 
                       currComment["link"] = commentLink;
                       feedComments[commentId].push(currComment);
               };
       };
       // Blogeintraege aus dem Inhalt der Seite heraussuchen.
       var content = $('content');
       var blogEntries = content.getElementsByClassName('blog-entry');
       for (var i = 0; i < blogEntries.length; i++) {
               // Nummer des Blogeintrages aus dem id Attribut extrahieren.
               var id = blogEntries[i].id;
               var entryId = id.substring(16, id.length);
               if (entryId != "") {
                       // Sind Kommentare vorhanden?
                       if (feedComments[entryId]) {
                               // Neuen Kommentar-Container erzeugen.
                               var comments = document.createElement('div');
                               comments.className = "comments_container";
                               // Fuer jeden Kommentar die benoetigten HTML Elemente anlegen.
                               for (j = 0; j < feedComments[entryId].length; j++) {
                                       var titleContainer = document.createElement('div');
                                       titleContainer.className = 'comment_title';
                                       var titleNode = document.createTextNode(feedComments[entryId][j]["title"]);
                                       titleContainer.appendChild(titleNode);
                                       var descriptionContainer = document.createElement('div');
                                       descriptionContainer.className = "comment_description";
                                       descriptionContainer.appendChild(feedComments[entryId][j]["description"]);
                                       var comment = document.createElement('div');
                                       comment.className = "comment_container";
                                       comment.appendChild(titleContainer);
                                       comment.appendChild(descriptionContainer);
                                       comments.appendChild(comment);
                               };
                               // HTML Elemente beim Blogeintrag anhängen.
                               blogEntries[i].appendChild(comments);
                       };
               };
       };
}

Verwendung

Um das Ganze nun wirklich verwenden zu können müssen die beiden JavaScript Funktionen in die "javascript.js"-Datei des Themes eingefügt werden. Um die Funktionen automatisch aufzurufen sobald die Seite geladen wurde, ist noch ein "onLoad" mechanismus notwendig. Da es unter Umständen noch weiter Funktionen gibt auf die das ganze zutrifft, verwende ich wieder eine Funktion, die ich mir beim Rico Projekt abgesehen habe. Ein ausführliche Beschreibung ist in meinem "RapidWeaver - Lightbox" Tutorial zu finden, wo ich den Mechanismus bereits verwendet habe.
Anschließend sind in RapidWeaver noch einige Ergänzungen an der Blogseite selbst notwendig. Das Proxy-Script muss als "Seitenanlage" unter "Erweiter" in den "Seiten-Information" der Blogseite hinzugefügt werden. Außerdem müssen unter "Code" in den "Seiten-Informationen" der Blogseite folgende Angaben in Feld "Eigenes JavaScript" angegeben werden:

var pageId = 'pageXX';
onloads.push(getHaloComments);

Die erste Zeile setzt die globale Variable mit der Page-ID der Blogseite, die zum herausfiltern Kommentare verwendet wird.
Die zweite Zeile sorgt dafür, dass der Kommentarimport in den "OnLoad" Mechanismus aufgenommen wird.
Wenn alles klappt werden die Kommentare kurz nach dem Laden der Seite unterhalb der Blogeinträge angezeigt.

Fazit

Mit Hilfe dieser Lösung kommt man dem richtigen Blog-Feeling wieder einen Schritt näher. Nachteil ist natürlich, das die Kommentare z.B. für Suchmaschinen nicht sichtbar sind. Ich denke aber, das der Seitentyp Blog in den kommenden Versionen von RapidWeaver weitere verbesserungen erhalten wird. Und wenn nicht, finde ich ja vielleicht doch noch einmal die Zeit, um micht in die Mac/RapidWeaver Programmierung einzuarbeiten und mir meine eigenes Blog Plugin zu bauen.
Ich hoffe auch dieses Tutorial trifft wieder den geschmack einiger RapidWeaver Benutzer und würde mich über Resonanz im zugehörigen Blog-Eintrag oder über das Kontaktformular freuen.

Werbung