Schlagwort-Archiv:

WordPress

Kommentare zitieren

Pink Butterfly Little Girl With Colorful Message Clouds. Isolate Ich habe mal wieder eine kleine Bastelei ins Blog eingebaut: eine Möglichkeit, einen ganzen Kommentar als Zitat ins Eingabefeld zu übernehmen. Dazu findet sich jetzt in der Kopfzeile jedes Kommentars am Ende ein entsprechender Link, der diesen als HTML samt umschließendem <blockquote> und darüber einer Zeile „Zitat von …“ samt Link zum Kommentar an die bisherige Eingabe anhängt – und nicht etwa beim Cursor einfügt; dies würde natürlich auch gehen (mach ich bei den Smilies ja schon), aber ich denke, bei so langen Einfügungen ist das Anhängen besser.

Das Script

Das ganze benötigt zum einen ein kleines Script (was auch bedeutet, dass Javascript aktiviert sein muss), das man entweder in eine externe Javascript-Datei einfügt oder einfach inline in die comments.php des Themes, am besten ziemlich direkt nach der Zeile

<?php if ( have_comments() ) : ?>

vor der Ausgabe der Kommentare.

Und das ist das Script, das jQuery benötigt – wird in vielen Fällen ja eh schon geladen:

<script type="text/javascript">
<!--
    function quotecomment (cID) {
        p = jQuery('#edit-comment'+cID);
        if ((!p) || (p.length==0)) p = jQuery('#comment-text-'+cID);
        t = p.html();
        t = t.replace(/<img [^>]*alt=[" ]*([^ "]*)[" ]*[^>]*>/ig,' $1 ');
        jQuery('#comment').val(jQuery('#comment').val() + '\n' +
            'Zitat von <a href="#comment-'+cID+'">'+
            jQuery('#comment-author-'+cID).text()+
            '</a>:\n<blockquote>'+t+'</blockquote>\n\n');
    }
//-->
</script>

In der Funktion (zu deren Aufruf wir gleich noch kommen) wird zunächst das Element mit der ID edit-commentXXX gesucht (mit XXX=Kommentarnummer), das den Kommentartext enthält, wenn das Plugin Ajax Edit Comments aktiv ist und dieser Kommentar bearbeitet werden kann; gibt’s das nicht, ist comment-text-XXX gefragt – das ist eine der kleinen Theme-Anpassungen, die ihr werdet vornehmen müssen – auch dazu gleich mehr.

Von diesem Element wird dann der HTML-Inhalt genommen – so bleiben die ganzen Formatierungen erhalten, auch wenn es mehr Tags enthält (etwa <p>), als original eingegeben wurde. Gut, alternativ hätte ich den Originalinhalt via Ajax nachladen können wie dieses Plugin (oder gleich das Plugin verwenden) oder ihn nochmal direkt in den Funktionsaufruf im Seitenquelltext schreiben, aber dies ist irgendwie die sparsamste Lösung…

Da in diesem HTML-Inhalt auch Bilder – selbst die Smilies – als img-Tags drin sind, müssen sie mit dem regulären Ausdruck im replace wieder in die Smilie-Codes (die in den alt-Attributen stehen) umgewandelt werden – Bilder dürft ihr ja nicht direkt in euren Kommentaren verwenden, das darf nur ich. :mrgreen: (Und natürlich muss dieser Ausdruck komplizierter sein, weil der Internet-Explorer wieder ein eigenes Süppchen kocht und etwa die Anführungszeichen bei den Attributen weglässt.)

(Benutzer einer Live-Vorschau, die bei <br> einen Zeilenumbruch zu viel einfügte, sollten die Zeile ergänzen, die ixiter unten im Kommentar nennt.)

Und schließlich wird in dem langen jQuery-Aufruf der Text/Code dann an den Inhalt des Eingabefeldes – das wie üblich die ID "comment" haben muss, ansonsten müsst ihr’s ändern – angehängt.

Änderungen in der Kommentarausgabe

In der Kommentarausgabe des Themes müssen, wie oben schon erwähnt, zum einen die Namen der Elemente mit ausgegeben werden, zum anderen der Link mit dem Aufruf der obigen Funktion. Das wird natürlich von eurem Theme abhängen, aber wer etwas Erfahrung mit solchen Basteleien hat, wird die passenden Stellen schon finden und geeignet ändern können.

Da wäre zum ersten der Name des Kommentators, der die entsprechende ID braucht – und zwar möglichst nur der Name und nichts drumrum. Man nimmt am besten einen eigenen span direkt um den Aufruf der WordPress-Funktion comment_author_link() (oder wie auch immer bei euch der Name mit Link ausgegeben wird); bei mir sieht da sowieso ein strong zur Fettschrift, deswegen hab ich das gleich verwendet:

<strong id="comment-author-<?php comment_ID(); ?>">
<?php comment_author_link() ?></strong>

Das muss, wie man sieht, im HTML-Kontext stehen (also außerhalb des <?php ... ?>-Bereichs). Dann brauchen wir den „zitieren“-Link an der gewünschten Stelle – und in dieser Form muss es innerhalb von <?php ... ?> stehen:

if ($comment->comment_type=="") {
    echo ' <span class="quotecomment">';
    echo '<a href="#commentform" onclick="quotecomment('.get_comment_ID().');" '.
        'title="den Inhalt dieses Kommentars ans Ende des Eingabefelds anh&auml;ngen (als HTML)">'.
        'zitieren <img src="/pics/quote.png" width="17" height="16" alt="" /></a>';
    echo '</span>';
}

Wer nicht will, dass beim Klick auch die Fensterposition wieder zum Eingabefeld scrollt, ersetze das href="#commentform" onclick="quotecomment… durch href="javascript:quotecomment… Wer das Bildchen nicht will, muss das entspr. img-Tag weglassen; ansonsten dürft ihr euch gerne mein Bildchen kopieren1.

Zu guter Letzt muss noch der eigentliche Kommentarinhalt seine ID erhalten, wozu wir den comment_text()-Aufruf in einen div packen, wenn noch kein geeigneter vorhanden ist (ansonsten den einfach entsprechend benennen oder, wenn der in eurem Theme schon eine ID hat, im Script oben anpassen):

echo '<div id="comment-text-'.get_comment_ID().'">';
comment_text();
echo '</div>';

So, das war’s dann eigentlich, ich hoffe, ich habe nichts vergessen. Wenn ihr noch Fragen habt, nur raus damit…

Ausblick

Als weitere Idee wäre noch das Zitieren beliebiger Textabschnitte aus einem Kommentar oder dem Beitrag durch Markieren des Textes, wie es etwa hier bei Frank Bültge vorgestellt wurde. Aber das muss auf einen späteren Zeitpunkt warten…


Foto: Alexandr Zinchevici – Fotolia.com

  1. bitte auf euren Webspace kopieren und nicht etwa hotlinken []

Kommentar-Anzahl anzeigen (ohne Plugin)

Kinder mit Abacus Wie meine Kommentatoren wissen, zeige ich bei jedem Kommentar an, wie viele Kommentare dieser Kommentator hier bisher geschrieben hat (genauer gesagt: wie viele unter demselben Namen abgegeben wurden; mehr zu dieser Unterscheidung später). Über die Details und die Neuerungen Ende letzten Jahres wollte ich schon länger mal schreiben, zumal auch manche andere davon profitieren könnten…

Ursprünglich geschah dies – natürlich ohne extra Plugin (und ohne Abacus ;) ) – mit einer einfachen Datenbankabfrage, die in der Kommentarausgabe aufgerufen wird:1

$c_count = (int) $wpdb->get_var(
   "SELECT COUNT(*) AS comments
    FROM $wpdb->comments
    WHERE comment_author='"
.esc_sql($comment->comment_author)."'
        AND comment_type=''
        AND comment_approved='1'"
);

(Hinweis: esc_sql ab WP 3.6; zuvor war’s $wpdb->escape)

Das Problem dabei: eine Datenbankabfrage pro Kommentar. Das ist kein so großes Problem, wenn es wenige Kommentare zum Beitrag oder insgesamt wenige Kommentare gibt. Aber je mehr Kommentare, je mehr verschiedene Kommentatoren und je seltenere Kommentatoren es gibt, umso mehr hat der Datenbankserver zu ackern und umso weniger kann ein Query-Cache – den wohl jeder MySQL-Server hat – oder ein DB-Cache-Plugin ausrichten. Und wenn der Beitrag zwar öfters, aber nicht ständig aufgerufen wird, fallen die Anfragen oder der Beitrag selbst auch immer wieder aus dem Cache raus.

Wenn ein Blog schon einige tausend Kommentare hat, ist diese Abfrage (gerade bei Shared Hosting) recht „teuer“ – ich hatte damals bei Tests Zeiten von bis zu 300 ms pro Anfrage (was ich in aktuellen Tests aber nicht mehr reproduzieren konnte, da waren’s nur ca. 30; keine Ahnung, was sich da geändert hat), was sich bei einem von vielen Leuten vielkommentierten Beitrag so darstellen kann, dass man sehen konnte, wie die Kommentare nach und nach „hingeblättert“ wurden…

Deswegen hab ich mir – nach einem kurzen Test mit einem simplen internen Cache, der sicher noch etwas schneller als eine Anfrage an den MySQL-DB-Cache ist – eine Lösung gebastelt, bei der alle Kommentaranzahlen für den jeweiligen Beitrag auf einmal eingelesen werden, was meinen Tests zufolge nicht wesentlich teurer ist als eine einzelne Abfrage der oben genannten Art:

$ag_number_of_comments_cache = array();
$ag_number_of_comments_inited = false;

function ag_number_of_comments ($comment, $before="",$after="") {
    global $wpdb,$ag_number_of_comments_cache,$ag_number_of_comments_inited;

    if (!$ag_number_of_comments_inited)
        ag_number_of_comments_post ((int) $comment->comment_post_ID);

    if ($comment->comment_type=="") {
        $c_count = (int) $ag_number_of_comments_cache[ mb_strtolower($comment->comment_author) ];
        echo $before."<span class=\"comments-by-author\">".($c_count==1?__('1 Comment'):$c_count." ".__('Comments'))."</span>".$after;
    }
}

function ag_number_of_comments_post ($post_ID) {
    global $wpdb,$wp_query,$ag_number_of_comments_cache,$ag_number_of_comments_inited;

    if ($post_ID==0) return;
    $ag_number_of_comments_inited = true;

    $comments = $wp_query->comments;
    $authors_sql = '0';
    foreach ($comments as $c) {
        if ( ($c->comment_type=='') && ($c->comment_approved=='1') ) {
            $authors_sql .= " OR comment_author='".esc_sql(mb_strtolower($c->comment_author))."'";
        }
    }
    $authorcounts = $wpdb->get_results(
       "SELECT comment_author, COUNT(*) as comments
        FROM $wpdb->comments
        WHERE comment_type=''
            AND comment_approved='1'
            AND ($authors_sql)
        GROUP BY comment_author"
);
    foreach ($authorcounts as $a) {
        $ag_number_of_comments_cache[mb_strtolower($a->comment_author)] = (int) $a->comments;
    }
}

Eingefügt wird dieser Code am besten natürlich in die functions.php des Themes (auf jeden Fall nach dem <?php am Anfang und vor dem ?> am Ende der Datei!) und aufgerufen dann an der geeigneten bzw. gewünschten Stelle in der Kommentarausgabeschleife in der comments.php2, etwa vor oder nach der Datumsausgabe, mit diesem PHP-Funktionsaufruf:

ag_number_of_comments ($comment);

Bzw. wenn es mitten im HTML-Code stehen soll, natürlich in PHP-Tags eingeschlossen:

<?php ag_number_of_comments ($comment); ?>

Mit den beiden zusätzlichen Parametern kann man noch etwas davor- und dahintergestellten Text angeben, für eine eingeklammerte Ausgabe etwa:

ag_number_of_comments ($comment, " (",") ");

Im Theme-CSS lässt sich das ganze dann mit der Klasse .comments-by-author ansprechen und stylen, falls gewünscht.

Zur eingangs erwähnten Zählung der Kommentare nur nach dem Namen möchte ich noch sagen, dass ich das zwar auch nicht optimal finde, da so mitunter verschiedene Leute, die mit demselben, i.d.R. „normalen“ Namen kommentieren, nicht unterschieden werden können. Nun könnte man zusätzlich die E-Mail-Adresse als Unterscheidungskriterium hinzufügen – doch wenn diese sich ändert (was bei ein paar Kommentatoren schon der Fall war), ist das Ergebnis auch nicht das gewünschte, genausowenig wie bei der URL, wo noch öfter „falsche Unterschiede“ auftreten, weil mal ein „/“ am Ende angegeben wurde und mal nicht. Deswegen belasse ich es beim Namen als Kriterium.

Übrigens wird die Groß-/Kleinschreibung auch nicht unterschieden – schon der Vergleich in MySQL kümmert sich nicht darum, weshalb eine entsprechende Umwandlung in PHP mittels mb_strtolower nötig ist; diese Funktion, die ordentlich mit Umlauten und Sonderzeichen umgehen kann, gibt’s ab PHP 4.3.0 (was heutzutage kein Problem sein sollte).

Nun denn, vielleicht hilft dieser Tip dem einen oder anderen. Es mag nicht der perfekte Code sein, denn auch wenn ich manchmal besserwisserisch korrigieren kann, bin ich kein PHP-Guru. :) Also sind Verbesserungsvorschläge sowie Anregungen und Fragen natürlich willkommen.

 


Foto: Monika Adamczyk – Fotolia.com

  1. Das Syntax-Highlighting des Codes hab ich hier online erstellt, musste dann aber noch die einzelnen Anführungszeichen durch &#39; ersetzen, damit WordPress sie nicht in ihre hübsche Variante umwandelt. Vielleicht finde ich fürs nächste Mal einen Highlighter, der auch das noch berücksichtigt… []
  2. Wenn ihr dort nur einen Aufruf der Funktion wp_list_comments() ohne Callback-Parameter findet, wird es etwas komplizierter – dann ließe es sich vielleicht in den Filter get_comment_date oder get_comment_author einhängen und somit vor/nach diesen ausgeben, aber das ist zu viel für diesen Beitrag; falls jemand ein Plugin dafür kennt – oder für die, die generell nicht gern im Code rumpfriemeln –, könnt ihr ja Bescheid sagen. []

Käsch

Rennhund Nur ein kleiner interner Hinweis: Hier im Blog läuft seit ein paar Stunden WP Super Cache, ein Plugin, das die erzeugten Seiten eine Zeit lang zwischenspeichert – für die Allgemeinheit als reine HTML-Dateien, sodass nicht mal die PHP-Engine anspringen muss – und so für weniger Serverlast sorgt. An sich solltet ihr nichts davon bemerken, außer dass die Seiten, die in letzter Zeit schon mal abgerufen wurden, schneller erscheinen. Wobei diejenigen, die hier schon mal kommentiert (und dadurch einen entsprechenden Cookie gesetzt) haben, nicht aus dem HTML-Cache („super cache“) bedient werden.

Die Anzeige der neuesten Kommentare in der Sidebar ist für die Nicht-Kommentatoren dann auf gecacheten Seiten wahrscheinlich nicht immer topaktuell, aber ich denke, das kann man verschmerzen. (Auch WP-PostViews zählt keine Suchmaschinen auf gecachten Seiten mehr.

Außerdem läuft seit einigen Tagen DB Cache Reloaded, das einzelne Datenbankabfragen zwischenspeichert – was den Vorteil hat, dass auch andere Seitenaufrufe profitieren als die, die vor kurzem stattgefunden hatten. Was mir allerdings aufgefallen ist, ist, dass der erste Aufruf der Admin-Seiten und das erste Schreiben eines Kommentars nach einiger Zeit relativ lange dauert – wohl weil DBCR dann die zahlreichen alten Cache-Dateien durchgeht und löscht; mir scheint, diese Garbage Collection ist noch nicht optimal…

Nun denn, falls euch irgendetwas Problematisches auffält, bitte Bescheid sagen. :)


Photo: TisseurDeToile/flickr, CC-Lizenz

Beitragsflut zieht Kreise…

Julias Beitragsflut durch Pluginfehler – 725 Beiträge gestern vormittag – blieb doch nicht so unbeachtet bzw. auf ihr Blog beschränkt, wie sie gehofft hatte. Johannes hatte sich noch am frühen Abend gewundert:

Und ich habe es verpasst – es kam komischerweise auch nichts darüber auf n-tv oder N24. :’(

Tja, Julias Bestechungsversuche („Johannes, die privaten Sender habe ich bestochen – die berichten darüber nicht!“) waren wohl zu gering, oder das Bestechungsgeld hat nicht bis zu den Abendnachrichten gereicht – hier ist der Beweis:

Julias Beitragsflut

Gut, dass es doch noch aufrichtige Nachrichtensender gibt!

Links der Woche (2008/51)