In Vorbereitung auf das Update musste ich ein paar Vorbereitungen treffen:
Ich deaktivierte des Plugins egg_anonymizer. Dies ist notwendig da Textpattern 4.7 alle Datenbankspalten entfernt hat die sich auf IP-Adressen beziehen. Eben diese Spalten adressiert aber egg_anonymizer um die Zugriffsdaten zu anonymisieren. Wenn man das Plugin nicht deaktiviert wird erhält man eine Reihe von Fehlern beim schreiben oder ändern von Artikeln und wahrscheinlich auch an anderen Stellen.
Vorsichtshalber habe ich auch alle angebotenen Updates für die installierten Sprachen eingespielt. Dazu muss man innerhalb von Textpattern im Menü unter „Administration“ und dann „Sprachen“ den entsprechenden Button bei jeder der betroffenen Sprache klicken.
Abschließend habe ich noch die empfohlenen Maßnahmen aus der Anleitung durchgeführt. In aller Kürze sind das Logout und Browsercache löschen.
Das eigentliche Upgrade habe ich streng nach Anleitung durchgeführt. Also anlegen eines Backups und einspielen der neuen Dateien. Wenn man eigene Änderungen, z.B. an der Datei .htaccess vorgenommen hat muss man diese in die neue Version reintegrieren. Beim Abschließenden Aufruf von Textpattern (nicht der damit erstellten Webseite) wird dann das Upgrade durchgeführt.
Bei mir verlief alles wie gewünscht und fehlerlos.
Nach dem Upgrade bietet es sich an den Menüpunkt „Diagnostics“ aufzusuchen und eventuelle Fehlermeldungen zu bearbeiten. In meinem Fall waren das zwei Stück:
Im Rahmen der neuen Unterstützung von Website-Themes wird ein neues Verzeichnis themes auf dem Webserver benötigt. Dieses hatte nach dem Upload der Dateien zu meinem Hoster nicht die nötigen Rechte. Das musste ich daher kurz anpassen.
Das zweite Problem besteht darin das der Update-Server von Textpattern nicht kontaktiert werden kann. Dies macht sich durch die Meldung „problem_connecting_update_server“ bemerkbar. Bislang konnte ich diesem Problem noch nicht nachgehen. Da ich aber den Textpattern-Blog registiert habe bekomme ich sowieso mit wenn es neue Versionen gibt.
Eine weitere Maßnahme um das Update zu prüfen ist in den Einstellungen die Seite in den Debug-Modus zu schalten. In diesem Modus erhält man zusätzliche Warnungen und Fehlermeldungen während man sich die Webseite ansieht. Am besten also in diesem Modus alle Arten von Seiten (Liste, Artikel, Suchergebnis und was man sonst so hat) aufrufen und sehen ob oben irgendwelche Meldungen auftauchen. Anschließend nicht vergessen den Modus wieder zurück zu stellen.
]]>Der Updateprozess ist in der Dokumentation von Textpattern beschrieben und unterscheidet sich nicht von dem Update auf Version 4.5.1 über das ich in einem Artikel vor einigen Jahren bereits geschrieben habe.
Achtung ist geboten wenn man, so wie ich, Anpassungen an einzelnen Dateien vorgenommen hat. Ich habe zusätzliche Konfigurationen in den beiden Dateien „.htaccess“ (eine davon im Unterverzeichnis „textpattern“) vorgenommen. Solche Anpassungen muss man in den neuen Dateien entsprechend nachziehen.
Bei mir trat diesmal ein Fehler auf: Das Updatescript das nach dem Einloggen gestartet wird konnte aufgrund von fehlenden Berechtigungen einen Ordner nicht löschen. Nachdem ich die entsprechenden Rechte kurzfristig vergeben hatte wurden wiederum Dateien nicht gefunden die im ersten Versuch bereits gelöscht worden sind. Ich konnte das Problem lösen indem ich die bemängelten Dateien aus dem Backup wiederhergestellt habe. Danach konnten die Dateien und der Ordner gelöscht werden. Abschießend habe ich die Rechte wieder zurückgesetzt.
Ein neues Feature von Textpattern 4.6 ist das Description-Feld. Hiermit ist es möglich zu jeder Seite (und anderen Elementen innerhalb von Textpattern) eine Beschreibung zu erfassen. Diese Beschreibung kann, ähnlich wie das bereits vorher vorhanden Feld für Keywords, in den Metaangaben des erzeugten HTML ausgegeben werden.
Bislang habe ich eines der benutzerdefinierten Felder für diese Aufgabe verwendet. Um zukünftig stattdessen das neue Feld nutzen zu können ist es nötig die bereits erfassten Beschreibungen zu kopieren. Das geht am leichtesten direkt auf der Datenbank. Das folgende SQL-Statement kopiert die Beschreibung aus der Spalte für das benutzerdefinierten Feld 10 in die neue Spalte.
UPDATE textpattern SET description = custom_10;
Danach kann man das die bisherigen Beschreibungen aus der ursprünglichen Spalte löschen:
UPDATE textpattern SET custom_10 = '';
Abschließend muss man in seinen Seitenvorlagen den bisherigen Code durch das neue Tag <txp:meta_description> ersetzen.
]]>Twitter versucht mit ihren Cards zusätzliche Informationen in Webseiten unterzubringen um diese in ihre eigenen Plattform nutzen zu können. Dazu haben sie drei Arten von Inhalten identifiziert die durch HTML-Seiten transportiert werden können. Ganz allgemein gehalten sind die sogenannten Summary Card. Hierunter versammelt sich alles was sich mit einem Titel und einer kurzen Beschreibung darstellen lässt – also ziemlich alles. Etwas spezieller sind die Photo Cards. Hiermit werden einzelne Bilder in den Mittelpunkt gerückt. Die dritte Art von Inhalten sind solche die sich abspielen lassen. Die Player Cards ermöglichen es Video- und Audiodateien direkt auf Twitter abspielen zu lassen.
Um die nötigen Informationen wie Titel, Beschreibung und URL des Bildes oder des Players für Twitter zugänglich zu machen definiert die Spezifikation feste Namen. Diese werden zusammen mit den Werten in Form von Meta-Elementen in beliebige HTML-Seiten eingebunden.
<html>
<head>
...
<meta name="twitter:card" content="summary">
<meta name="twitter:url" content="http://human-injection.de/articles/twittercards">
<meta name="twitter:title" content="Twitter Cards mit Textpattern">
<meta name="twitter:description" content="Twitter Cards als Plugin für Textpattern">
</head>
<body>
...
</body>
</html>
Das ganze erinnert an das Open Graph protocol das von Facebook zu ähnlichem Zweck entwickelt wurde. Twitter geht sogar soweit, die Informationen aus dem Open Graph als Fallback heranzuziehen wenn eine Twitter Card nicht vorliegt oder nicht vollständig ist.
Ein alleiniges Merkmal der Twitter Cards ist es das man zwei Twitter-Accounts mit seinen Webseiten verknüpfen kann. Man gibt mit site den Herausgeber und mit creator den Urheber eines Textes, Bildes oder Videos an:
<meta name="twitter:site" content="@HumanInjection">
<meta name="twitter:creator" content="@SebastiansIT">
Wenn nun der Link zu einer derart präparierten HTML-Seite getwittert wird holt sich Twitter mit seinem Twitterbot die Informationen und stellt sie in ihren Clients da, auch in den Mobilen. Damit das aber auch wirklich geschieht muss man sich zur Zeit noch für die Teilname an diesem Prozess bewerben. Twitter will sich hier wohl noch nicht zu weit vorwagen und jedermann Tür und Tor öffnen.
An dieser Stelle möchte ich empfehlen kurz zur Spezifikation der Cards zu wechseln und sich dort einen besseren Überblick über das Format und seine Möglichkeiten zu verschaffen.
Da ich einfach nicht widerstehen kann wenn es um zusätzliche Meta-Elemente in meinen Webseiten geht hatte ich endlich einen Vorwand um ein Plugin für Textpattern (das hier eingesetzte CMS) zu schreiben.
Wer mag kann das Ergebnis shs_twittercards herunterladen und installieren. Den Quellcode gibt es auch. Das Plugin steht unter einer GNU General Public License. Wer nun wissen möchte wie ich das gemacht habe kann jetzt weiterlesen.
Für den Einstieg in Textpattern und dessen Plugins kann ich das Buch Textpattern Solutions empfehlen. Hier wird alles beschrieben was man für den Einstieg braucht.
Mein Plugin ist eigentlich ganz einfach. Zu erst wir ein TXP-Tag shs_twittercards durch eine gleichnamige Funktion definiert.
function shs_twittercards($atts) {
...
}
Diesem Tag muss nun in die Lage versetzt werden die Informationen für die Cards aufzunehmen. Dies erfolgt über Attribute – eines für jeden spezifizierten Wert. Alles was nicht angegeben wird, wird durch das Plugin mit Standartwerte initialisiert.
<txp:shs_twittercards site="@humaninjection" creator="@SebastiansIT" />
Der folgende Quellcode erzeugt aus den Attributen Variablen und belegt diese wenn nötig mit den Standartwerten
function shs_twittercards($atts) {
...
extract(lAtts(array(
'onlytwitteragent' => '0',
'cardtype' => 'summary',
'site' => '',
'siteid' => '',
'creator' => '',
'creatorid' => '',
'url' => processTags('permlink', ''),
'description' => $GLOBALS['thisarticle']['excerpt'],
'title' => $GLOBALS['thisarticle']['title'],
'image' => '',
'ignorearticleimage' => '0',
'imagedefault' => '',
'imagewidth' => '',
'imageheight' => '',
'player' => '',
'playerwidth' => '',
'playerheight' => '',
'playerstream' => '',
'playerstreamcontenttype' => ''
), $atts));
...
}
Wie man sehen kann sind einige dieser Standartwerte mit passenden Feldern eines Artikels belegt. Zum Beispiel wird der Titel des Artikels gleichzeitig als Titel der Card genutzt. Der Zugriff auf diese Felder erfolgt über globale Variablen die Textpattern beim aufrufen eines Artikels anlegt.
...
'title' => $GLOBALS['thisarticle']['title'],
...
Andere Standartwerte ergeben sich aus der Ausgabe von anderen TXP-Tags. Diese kann man in einem Plugin aufrufen indem man die Funktion processTags(string, string) mit dem Namen des Tags und ihren Attributen aufruft. Das folgende Beispiel erzeugt die URL eines Artikels indem es das Tag <txp:permlink />
aufruft.
processTags('permlink', '')
Einzig die Angabe zum Bild der Card muss noch speziell aufbereitet werden. Im ersten Schritt wird dazu die Quelle bestimmt. Priorität hat die Angabe aus dem Image-Attribut. Wenn diese nicht vorliegt wird das Artikelbild herangezogen. Fehlt auch dies wird der Wert zum Standardbild verwendet.
// Check the article_image for
// a) a picture is set
if ($image != '') {
$image_url = $image;
}
// b) a article image exists
else if ($GLOBALS['thisarticle']['article_image'] != '') {
$image_url = $GLOBALS['thisarticle']['article_image'];
}
// c) a default image is set
else if ($imagedefault != '') {
$image_url = $imagedefault;
}
else {
$image_url = '';
}
Danach muss die vorliegende Art der Referenzierung auf das Bild (ID-Nummer des Bildes, relative und absolute URL) in eine absolute URL umgewandelt werden. Dies geschieht im Fall einer ID-Nummer durch anwenden des TXP-Tags <txp:image_url link="0" />
. Relative URL’s werden hingegen mit UrlToAbsolute, einer PHP Bibliothek unter BSD-Lizenz, umgewandelt.
//Convert the actual image_url value to an absolute url
if ($image_url != '') {
// a) a picture ID
if (is_numeric($image_url)) {
$image_url = processTags('image_url', 'id="'.$image_url.'" link="0"');
}
// b) a relative URL (absolute URL's starts width 'http://' or 'https://')
else if (!strpos($image_url, 'http://')
&& !strpos($image_url, 'https://')) {
$baseUrl = $_SERVER['HTTP_HOST'] . $GLOBALS['pretext']['request_uri'];
if (array_key_exists('HTTPS', $_SERVER)) {
$baseUrl = 'https://' . $baseUrl;
}
else {
$baseUrl = 'http://' . $baseUrl;
}
$image_url = url_to_absolute($baseUrl, $image_url);
}
// c) a absolute URL
else {
//do nothing
//$image_url = $image_url
}
}
Nachdem nun alle Variablen vorbereitet sind werden, falls ein Wert vorhanden ist, die entsprechenden Meta-Element erzeugt.
function shs_twittercards($atts) {
...
$returnvalue = '<!-- Twitter Card --> ';
$returnvalue = $returnvalue.'<meta name="twitter:card" content="'.$cardtype.'"> ';
if ($site != '' && $siteid == '')
$returnvalue = $returnvalue.'<meta name="twitter:site" content="'.$site.'"> ';
if ($siteid != '')
$returnvalue = $returnvalue.'<meta name="twitter:site:id" content="'.$siteid.'"> ';
if ($creator != '' && $creatorid == '')
$returnvalue = $returnvalue.'<meta name="twitter:creator" content="'.$creator.'"> ';
if ($creatorid != '')
$returnvalue = $returnvalue.'<meta name="twitter:creator:id" content="'.$creatorid.'"> ';
if ($url != '')
$returnvalue = $returnvalue.'<meta name="twitter:url" content="'.$url.'"> ';
if ($description != '')
$returnvalue = $returnvalue.'<meta name="twitter:description" content="'.trim(strip_tags($description)).'"> ';
if ($title != '')
$returnvalue = $returnvalue.'<meta name="twitter:title" content="'.$title.'"> ';
if ($image_url != '') {
$returnvalue = $returnvalue.'<meta name="twitter:image" content="'.$image_url.'" /> ';
if ($imagewidth != '') {
$returnvalue = $returnvalue.'<meta name="twitter:image:width" content="'.$imagewidth.'" /> ';
}
if ($imageheight != '') {
$returnvalue = $returnvalue.'<meta name="twitter:image:height" content="'.$imageheight.'" /> ';
}
}
if ($player != '') {
$returnvalue = $returnvalue.'<meta name="twitter:player:height" content="'.$player.'"> ';
$returnvalue = $returnvalue.'<meta name="twitter:player:width" content="'.$playerwidth.'"> ';
$returnvalue = $returnvalue.'<meta name="twitter:player:height" content="'.$playerheight.'"> ';
}
if ($playerstream != '') {
$returnvalue = $returnvalue.'<meta name="twitter:player:stream" content="'.$playerstream.'"> ';
$returnvalue = $returnvalue.'<meta name="twitter:player:stream:content_type" content="'.$playerstreamcontenttype.'"> ';
}
...
}
Zum Schluss werden die gesammelten Elemente aus der Funktion zurückgegeben und damit auf der Webseite ausgegeben.
function shs_twittercards($atts) {
...
return $returnvalue;
}
Um das unnötige “Zumüllen” des HTML-Codes mit Metainformationen zu vermeiden habe ich eine kleine Besonderheit eingebaut die die Twitter Card nur dann erzeugt wenn der HTTP-Request vom Twitterbot ausgeht. Normale Browser bekommen so von der Card nichts zu sehen und sparen sich also die paar KB am Downloadvolumen.
function shs_twittercards($atts) {
...
if ( isTwitterAgent() || $onlytwitteragent == '0') {
//Create the Twitter Card Meta Elements
...
}
return $returnvalue;
}
Dieses Verhalten lässt sich abstellen (z.B. fürs Debuggen) indem man das Attribute onlytwitteragent auf den Wert 0 setzt.
<txp:shs_twittercards onlytwitteragent="0" />
Herauszufinden ob der Twitterbod den Request ausgelöst hat ist Aufgabe der Funktion isTwitterAgent(). Sie prüft den User Agent um den Crawler von Twitter zu identifizieren.
function isTwitterAgent() {
if ( stristr($_SERVER["HTTP_USER_AGENT"],"Twitterbot") ) {
return true;
}
else {
return false;
}
}
]]>Zu den Vorbereitungen eines Updates gehört natürlich immer ein Backup. Sowohl das Dateisystem als auch die Datenbank der bestehenden Installation müssen dabei berücksichtigt werden.
Als nächstes habe ich die Übersetzungen für das Backend auf den aktuellen Stand gebracht. Das kann man ganz einfach unter Administration -> Einstellungen -> Sprachen verwalten. Dort kann man für alle installierten Sprachen ein Update durchführen.
Da ich die Admin-Oberfläche mit einem zusätzlichen Theme ausgestattet hatte, habe ich die entsprechende Einstellung wieder auf Classic geändert. Sollte sich durch das Update am Markup etwas ändern, würde dadurch wenigstens nicht alles Unformatiert sein. Ob es wirklich geholfen hat kann ich aber nicht sagen.
Der Updateprozess ist ist auf Textpattern.net beschrieben. Im Grunde genommen besteht er nur darin einige Ordner aus der neuen Version in den Webspace zu kopieren und damit die bestehenden Dateien zu ersetzen. Nach dem dies geschehen ist werden beim nächsten Login automatisch einige SQL-Scripte ausgeführt die auch die Datenbank anpassen.
Beim ersten Aufruf des Backends greifen noch die alten CSS-Styles – alles sieht ziemlich wirr aus. Ein Reload der Seite schafft hier Abhilfe.
Damit ist eigentliche Update abgeschlossen, mal sehen ob noch alles funktioniert.
Um mögliche Probleme schnell Identifizieren zu können sollte man den sogenannten Produktions Status in den Einstellungen auf Debug setzen. Dadurch wird einem jedes Problem beim Aufruf der eigenen Seite angezeigt. Dadurch ist mir aufgefallen das dass Plugin chh_keywords nicht mehr richtig arbeitet.
Beim Auflisten von Artikeln mit einem bestimmten Keyword wird eine Fehlermeldung wie diese ausgegeben:
Undefined variable: uExpires
Bei meiner Suche nach einer Lösung bin ich auf einen Forumseintrag gestoßen der empfiehlt die fehlenden Variablen im Aufruf der Methode safe_rows zu ergänzen. Daraufhin habe ich Zeile 193 der zum Plugin gehörenden Bibliothek chh_article_lib wie folgt geändert:
$rs = safe_rows_start('*, unix_timestamp(Posted) AS uPosted,
unix_timestamp(LastMod) as uLastMod,
unix_timestamp(Expires) as uExpires' . $columns,
'textpattern', $where);
Mit dieser kleinen Modifikation gibt es jetzt auch keine Fehlermeldungen mehr.
]]><?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" …>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
Damit der Server auch noch den richtigen Mimetype ausliefert muss man auch noch eine der Dateien von Texptattern verändern. Für Textpattern 4.0.3 wird das auf textpattern.org beschrieben. Mit Version 4.2 hat es bei mir aber auch funktioniert.
Diese Änderung schließt den Internet Explorer übrigens nicht ganz aus da Sie eine Weiche für Programme enthält die XHTML nicht verstehen (wollen).
]]>Solche ähnlichen Seiten können entstehen durch Verwenden von HTTP und HTTPS, durch “www” als Hostname oder eben nicht, durch unterschiedliche Groß-/Kleinschreibung der URI‘s und viele andere Umstände.
Da wir alle etwas davon haben – nämlich bessere Suchergebnisse durch weniger Dopplungen – sollte man das Canonical-Tag vorsorglich einbauen. Das ist auch ganz einfach:
<link href="http://human-injection.de/..."
rel="canonical" />
Die Wert von “href” sollte dabei immer der bevorzugten URI eurer jeweiligen Seite entsprechen.
Zur Visualisierung eines Canonical-Tags bietet sich der Firefox an: Dort wird von Haus aus ein Symbol in der Adressleiste angezeigt wenn ein solches Tag in der Angezeigten Seite gefunden wurde. Wenn der Hintergrund dieses Symbols Blau ist weicht die aktuelle URI von der angegebenen ab, grau bedeutet eine Übereinstimmung.
zur Integration des Canonical-Tags in Textpattern habe ich basierend auf einem Artikel auf der Webseite TXP Tips ein kleines Monster aus Fallunterscheidungen geschrieben um alle, für mich relvanten Fälle abzudecken:
<txp:if_individual_article>
<!-- Canonical Tag for Articles --> <link rel="canonical" href="<txp:permlink />" />
<txp:else /> <txp:if_search>
<!-- Canonical Tag for Search-Result --> <link rel="canonical" href="<txp:site_url /> ?q=<txp:search_term />" />
<txp:else /> <txp:if_section name=""> <txp:if_category>
<!-- Canonical Tag for category page in default section --> <link rel="canonical" href="<txp:site_url /> kategorie/<txp:category />/" />
<txp:else /> <txp:chh_if_keywords>
<!-- Canonical Tag for keyword page in default section --> <link rel="canonical" href="<txp:site_url />keywords/ <txp:chh_keywords_value />" />
<txp:else />
<!-- Canonical Tag for Homepage --> <link rel="canonical" href="<txp:site_url />" />
</txp:chh_if_keywords> </txp:if_category> <txp:else /> <txp:if_category>
<!-- Canonical Tag for Category in Section --> <link rel="canonical" href="<txp:site_url /><txp:section /> /?c=<txp:category />" />
<txp:else />
<!-- Canonical Tag for Section --> <link rel="canonical" href="<txp:site_url /> <txp:section />/" />
</txp:if_category> </txp:if_section> </txp:if_search> </txp:if_individual_article>
Das deckt die Fälle Startseite, Sektion, Kategorie, Kategorie in Sektion, Keyword, Artikel und Suche ab. Ergänzen könnte man noch die Autorenseiten.
]]>Das Plug-In upm_savenew erleichtert das Kopieren von Artikeln indem es einen entsprechenden Button ergänzt. Nichts Großartiges, aber hilfreich.
chh_keywords ermöglicht es die zu einem Artikel vergebenen Keywords/Tags als Listen oder Tagclouds auszugeben. Außerdem kann man damit Listen mit Artikeln zu einem Schlagwort erzeugen.
Ähnlich dem Tag <txp:category_list> von Textpattern erzeugt <txp:cbs_categorylist> Kategorielisten. Der entscheidende Unterschied ist, dass Kategorien die keine Artikel enthalten ausgeschlossen werden.
Zur automatischen Erzeugung von Sitemaps gemäß der Spezifikation auf sitemaps.org verwende ich das Plugin rah_sitemap. Es bietet umfangreiche Einstellungsmöglichkeiten was in die Sitemap aufzunehmen ist und was nicht.
Eine Alternative, die ich aber noch nicht ausprobiert habe, ist jmd_sitemap.
Mit rah_post_versions, einem Plugin für das Admin-Interface von Textpattern, soll es möglich sein alle Änderungen, die man z.B. an Artikeln oder Templates durchgeführt hat, wieder rückgängig zu machen, indem alle früheren Versionen gespeichert werden.
Ich habe es noch nicht ausprobiert, werde es aber möglichst bald mal Testen.
Wenn man Artikel verfasst, stolpert man oft darüber, dass das Eingabefeld für den Text einen eigenen Scrollbalken hat. Bei Benutzung des Scrollrades an der Maus weiß man so oft nicht was man da eigentlich Scrollt – Die Seite oder den eingegebenen Text. rah_autogrowing_textarea macht damit Schluss, indem es die Eingabefelder dynamisch anpasst und so kein zusätzlicher Scrollbalken benötigt wird.
]]>