<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Code-Notizen</title>
	<atom:link href="http://code.freudendahl.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://code.freudendahl.net</link>
	<description>Have you tried turning it off and on again?</description>
	<lastBuildDate>Tue, 08 May 2012 00:12:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>Rails-Entwicklung in einer Ubuntu-VM</title>
		<link>http://code.freudendahl.net/2012/05/rails-entwicklung-in-einer-ubuntu-vm/</link>
		<comments>http://code.freudendahl.net/2012/05/rails-entwicklung-in-einer-ubuntu-vm/#comments</comments>
		<pubDate>Mon, 07 May 2012 22:58:35 +0000</pubDate>
		<dc:creator>Johannes</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Ruby-on-Rails]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://code.freudendahl.net/?p=100</guid>
		<description><![CDATA[Ich arbeite normalerweise unter Windows 7, wollte mich nun aber doch auch privat mal an einem Rails-Projekt versuchen. Das geht zwar auch unter Windows, aber ich war nicht speziell scharf darauf, auszutüfteln was man dafür alles modifizieren muss. Rails-Entwicklung geht unter Linux doch einfach flüssiger von der Hand. Also habe ich mir eine Arbeitsumgebung in einer Ubuntu-VM eingerichtet, die ich in diesem Beitrag näher beschreiben möchte. Das Herzstück der neuen &#8230; <a href="http://code.freudendahl.net/2012/05/rails-entwicklung-in-einer-ubuntu-vm/">Weiterlesen <span class="meta-nav">&#8594;</span></a><p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></description>
			<content:encoded><![CDATA[<p><img src="http://code.freudendahl.net/files/2012/05/ubuntu-vm1.jpg" alt="Ubuntu-VM" title="Ubuntu-VM unter Windows 7" width="250" height="186" class="alignright size-full wp-image-102 colorbox-100" />Ich arbeite normalerweise unter Windows 7, wollte mich nun aber doch auch privat mal an einem Rails-Projekt versuchen. Das geht zwar auch unter Windows, aber ich war nicht speziell scharf darauf, auszutüfteln was man dafür alles modifizieren muss. Rails-Entwicklung geht unter Linux doch einfach flüssiger von der Hand. Also habe ich mir eine Arbeitsumgebung in einer Ubuntu-VM eingerichtet, die ich in diesem Beitrag näher beschreiben möchte.</p>
<p>Das Herzstück der neuen Arbeitsumgebung ist eine Virtuelle Maschine, in der ein Ubuntu-System läuft. In Ubuntu steht die gewohnte Konsole zur Verfügung. Hier laufen RVM, Git, Ruby, Thin oder ein anderer Webserver der Wahl. Was ich weiterhin in Windows ansiedeln wollte (und hier kamen erste Schwierigkeiten auf), waren der Browser und die Datenbank. Den Firefox kann ich natürlich auch in der VM starten, aber das macht sich außerhalb doch etwas praktischer. Die Datenbank läuft unter Windows, da die VM nicht zu sehr wachsen soll und ich unter Windows auch schon entsprechende Datenbank-Tools eingerichtet habe.</p>
<h3>Virtuelle Maschine</h3>
<p>Ich habe es kurz mit <a href="http://wiki.qemu.org/Main_Page">QEMU</a> versucht, es aber leider nicht geschafft, darin Ubuntu zu installieren. Keine Ahnung wieso, ich bin dann jedenfalls bei <a href="https://www.virtualbox.org/">Virtual Box</a> gelandet und damit sehr zufrieden.</p>
<p>Also Schritt 1: Virtual Box in der passenden Windows-Version herunterladen und installieren.</p>
<p>Schritt 2: Eine neue virtuelle Maschine anlegen. Den Container mitwachsend gestalten und das Limit lieber zu groß als zu klein wählen. Das kann meines Wissens nach später nur noch mit viel Frickelei vergrößert werden, wenn die virtuelle Platte doch überläuft. Ich habe aktuell 15 GB eingestellt und belege davon im Moment 6. Hätte ich vielleicht sogar schon mehr eintragen sollen. Ansonsten ist nichts spezielles einzustellen, die Defaults reichen aus.</p>
<p>Schritt 3: Ein Betriebssystem in der VM installieren. Ich habe mir dafür eine 32-Bit-Version von <a href="http://www.ubuntu.com/">Ubuntu</a> heruntergeladen, und zwar als *.iso-Datei. Diese Datei habe ich dann in der Konfiguration der VM als CD eingehangen (unter &#8222;Massenspeicher&#8220;) und natürlich ausgewählt, dass das System von CD auch booten soll. Wichtig: Die CD muss als IDE-Controller angelegt werden, SATA funktioniert nicht. Dann einfach die VM starten und wie gewohnt Ubuntu installieren.</p>
<p>Falls die System-übergreifende Zwischenablage nicht auf Anhieb funktioniert, braucht ihr wahrscheinlich die GuestAdditions im Gast-Betriebssystem. So ganz bin ich da auch noch nicht dahintergestiegen, aber seit ich sie installiert habe, klappt es nun, Texte in Windows zu kopieren und in Ubuntu einzufügen, was für ein sinnvolles Arbeiten unerlässlich ist.</p>
<h3>Netzwerk-Konfiguration</h3>
<p>Etwas mehr Zeit habe ich mit der Konfiguration des Netzwerks verbracht. Standardmäßig kann die VM aufs Internet zugreifen, über den vorhandenen NAT-Adapter. Das ist gut so, denn wir müssen ja Software aktualisieren, Gems installieren etc.</p>
<p>Die VM muss aber auch auf den Windows-Host zugreifen können, zum Erreichen der Datenbank, und der Windows-Host muss die VM erreichen können, zum Darstellen der Anwendung im Browser. Dazu brauchen wir einen zweiten Netzwerk-Adapter, und zwar vom Typ &#8222;Host-Only&#8220;. Die VM einfach ausschalten und über &#8222;Ändern&#8220; die Einstellungen bearbeiten. Unter &#8222;Netzwerk&#8220; einen weiteren Adapter vom Typ &#8222;Host-Only&#8220; hinzufügen. Dann die VM wieder starten.</p>
<p>Unter Windows ist nun ein weiteres Netzwerk verfügbar (Eingabeaufforderung &gt; &#8222;ipconfig&#8220;), welches die Adresse &#8222;192.168.56.1&#8243; hat. In Ubuntu existiert analog ein Netzwerk &#8222;eth1&#8243; mit der IP-Adresse &#8222;192.168.56.101&#8243; (Terminal &gt; &#8222;ifconfig&#8220;). Leider klappte es bei mir nicht, dass ich von Ubuntu aus auf die Datenbank unter Windows zugreifen konnte. Nach einiger Recherche stellte sich heraus, dass Windows das neue Netzwerk nicht identifizieren kann und es deswegen als öffentlich behandelt. Eingehende Anfragen werden dann von der Firewall geblockt. Schaut man sich das Netzwerk- und Freigabecenter an, sollte das &#8222;Host-Only&#8220;-Netzwerk dort als unidentifiziert und mit dem Parkbank-Symbol auftauchen. Man kann den Status des Netzwerks allerdings dort auch nicht ändern, was ärgerlich ist.</p>
<p><img src="http://code.freudendahl.net/files/2012/05/ubuntu-vm2.jpg" alt="IPv4-Konfiguration" title="IPv4-Konfiguration" width="300" height="334" class="alignleft size-full wp-image-103 colorbox-100" />Die Lösung: Man muss dem Netzwerk ein Gateway geben, dann arbeitet Windows damit auch freiwillig. Warum das so ist kann ich als Netzwerk-Laie nicht sagen, klappt aber. Im Netzwerk- und Freigabecenter rechts auf &#8222;VirtualBox Host-Only-Netzwerk&#8220; klicken und im PopUp-Dialog auf &#8222;Eigenschaften&#8220;. &#8222;Internetprotokoll Version 4&#8243; auswählen und auf &#8222;Eigenschaften&#8220; klicken. IP-Adresse und Subnetzmaske waren bei mir eingetragen, DHCP und Gateway nicht. Als Gateway muss ein existierender <em>anderer </em>Rechner im Netzwerk ausgewählt werden, hier also die IP der Ubuntu-VM eintragen. Beim Speichern fragt Windows nun plötzlich, was für ein Netzwerktyp das Host-Only-Netzwerk sein soll. Hier privat oder Arbeit nach Bedarf auswählen, dann klappt die Verbindung aus der VM heraus.</p>
<p>In der database.yml in den Rails-Projekten muss nun natürlich noch der Host eingetragen werden. Einfach &#8222;host: 192.168.56.1&#8243; ergänzen.</p>
<h3>Letzte Schritte</h3>
<p>Eclipse in der VM zu starten habe ich mich nicht getraut. Als Editor verwende ich dort fürs erste <a href="http://www.sublimetext.com/">Sublime</a>, von dem meine Kollegen sehr begeistert sind. Nun ja, das Programm läuft schlank und schnell, ist also prinzipiell brauchbar. Ich muss noch sehen, ob ich damit auf Dauer so gut klarkomme, dass ich es mir kaufen werde. Alternative Vorschläge an Tools für die Rails-Entwicklung nehme ich gerne entgegen! <img src='http://code.freudendahl.net/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley colorbox-100' /> </p>
<p>Die Einrichtung von <a href="https://rvm.io/">RVM</a>, Rails, Git etc. beschreibe ich jetzt nicht im Detail, da gibt es bessere Anleitungen im Netz. Zur Versionskontrolle nehme ich wie schon für meine <a href="http://code.freudendahl.net/2012/04/lokale-wordpress-installation/">WordPress-Projekte</a> beschrieben Git mit <a href="https://bitbucket.org/">bitbucket</a>.</p>
<p>Als letztes dann noch eine kleine Modifikation, um sich für den Aufruf aus Windows heraus nicht die IP-Adresse merken zu müssen: Wie schon für meine <a href="http://code.freudendahl.net/2012/04/lokale-wordpress-installation/">lokalen WordPress-Installationen</a> richte ich mir eine lokale Domain für das neue Projekt ein. Die <a href="http://de.wikipedia.org/wiki/Hosts-Datei">HOSTS-Datei</a> öffnen (bei mir unter C:\Windows\System32\drivers\etc) und das hier ergänzen:</p>
<pre class="brush: plain; title: ; notranslate">192.168.56.101   my_ubuntu.vm</pre>
<p>Das Rails-Projekt aus der VM lässt sich dann in Windows mit http://my_ubuntu.vm:3000/ aufrufen. Da Rails-Projekte üblicherweise immer direkt mit Port 3000 gestartet werden, könnte ihr theoretisch einfach einen solchen Eintrag machen und für beliebig viele Projekte nutzen. Falls die Passwörter der Projekte verschieden sind, macht es sich aber ggf. besser, pro Projekt eine solche Domain einzurichten (einfach immer die gleiche IP nehmen). Dann kommt der Passwort-Manager im Browser auch nicht durcheinander.</p>
<p>So, ich hoffe, diese Anleitung war für den ein oder anderen hilfreich. Tips, was noch besser geht, sind willkommen. Und nein, &#8222;ganz auf Ubuntu umsteigen&#8220; ist nicht direkt hilfreich. <img src='http://code.freudendahl.net/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley colorbox-100' /> </p>
<p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></content:encoded>
			<wfw:commentRss>http://code.freudendahl.net/2012/05/rails-entwicklung-in-einer-ubuntu-vm/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Ruby-on-Rails: Cookie-Detection</title>
		<link>http://code.freudendahl.net/2012/04/ruby-on-rails-cookie-detection/</link>
		<comments>http://code.freudendahl.net/2012/04/ruby-on-rails-cookie-detection/#comments</comments>
		<pubDate>Thu, 26 Apr 2012 19:26:43 +0000</pubDate>
		<dc:creator>Johannes</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Ruby-on-Rails]]></category>

		<guid isPermaLink="false">http://code.freudendahl.net/?p=97</guid>
		<description><![CDATA[Ich schreibe hier viel über WordPress, verbringe meine Arbeitstage im Moment aber zu einem Großteil mit Ruby-on-Rails. Damit das hier endlich auch mal vorkommt, habe ich mir mal ein interessantes Problem herausgegriffen, das kürzlich zu lösen war: erkennen ob der Besucher Cookies aktiviert hat oder nicht. In dem betroffenen Projekt basiert die Session auf Cookies. Ohne Cookies keine Session, und ohne Session kein Login und keine Registrierung. Von Haus aus &#8230; <a href="http://code.freudendahl.net/2012/04/ruby-on-rails-cookie-detection/">Weiterlesen <span class="meta-nav">&#8594;</span></a><p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></description>
			<content:encoded><![CDATA[<p>Ich schreibe hier viel über WordPress, verbringe meine Arbeitstage im Moment aber zu einem Großteil mit Ruby-on-Rails. Damit das hier endlich auch mal vorkommt, habe ich mir mal ein interessantes Problem herausgegriffen, das kürzlich zu lösen war: erkennen ob der Besucher Cookies aktiviert hat oder nicht.</p>
<p>In dem betroffenen Projekt basiert die Session auf Cookies. Ohne Cookies keine Session, und ohne Session kein Login und keine Registrierung. Von Haus aus ging das leider still und leise schief. Man logt sich ein, das System setzt den Cookie und schickt einen zur internen Startseite. Dort fehlt aber der Cookie, also kommt man zurück zur Loginseite. Kein Hinweis, wieso der Login nicht geklappt hat. Hier sollte dem Nutzer ein verständlicher Hinweis angezeigt werden.</p>
<p>Eine kurze Google-Suche ergab in erster Linie diesen vergleichsweise alten <a href="http://kill-0.com/duplo/2007/07/12/rails-cookie-detection/">Blog-Beitrag</a> sowie <a href="http://clearcove.ca/2009/09/rails-cookie-detection/">diese Anleitung</a> von 2009. Letzteres sah sehr gut aus, war aber noch nicht ganz, was wir brauchten. Insbesondere wird hier bei abgeschalteten Cookies die Warnung auf einer extra Seite angezeigt (/cookie-test). Schöner ist es dagegen, wenn das auf der jeweils betroffenen Seite als Flash-Message passiert.</p>
<p>Ich habe das also wie folgt ein wenig umgebaut. Die Hauptarbeit erledigt lib/cookie_detection.rb:</p>
<pre class="brush: ruby; title: ; notranslate">
# -*- coding: utf-8 -*-
# Detects if cookies are present (only GET requests, not for bots).
# If cookies are disabled, shows a flash message.
# Usage:
# * save this file at a location in your rails app where it gets required
#   (e.g. RAILS_ROOT/lib)
# * application_controller:
#   include CookieDetection
# * relevant controllers:
#   before_filter :cookies_required
#
# Adapted from here: http://clearcove.ca/2009/09/rails-cookie-detection/
module CookieDetection
  # Checks if cookies are turned on in the user's browser.
  def cookies_required
    # check if anything needs to be done at all
    if !cookies.entries.empty? ||                       # skip if a cookie is set
       (request &amp;&amp; request.request_method != 'GET') ||  # skip non-GET requests
       is_megatron?(request.env[&quot;HTTP_USER_AGENT&quot;])     # skip requests from bots
      return true
    end

    # set a flash message on the second call
    if params[:cookie_test].present?
      logger.warn(&quot;=== cookies are disabled&quot;)
      flash[:alert] = 'Sie benötigen Cookies um sich einzuloggen oder zu registrieren. Bitte stellen Sie sicher, dass Ihr Browser Cookies akzeptiert.'
      return true
    end

    # otherwise set a cookie and redirect to the current URL + parameter
    cookies[&quot;cookie_test&quot;] = Time.now
    url = request.url + (request.url.index('?').present? ? '&amp;' : '?') + 'cookie_test=' + Time.now.to_i.to_s
    redirect_to(url)
  end

  # from: http://gurge.com/blog/2007/01/08/turn-off-rails-sessions-for-robots/
  # added bingbot + a generic bot for new bots
  def is_megatron?(user_agent)
    user_agent =~ /\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|bingbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg|bot)\b/i
  end
end
</pre>
<p class="tested_with">Getestet mit: Ruby 1.9.3 / Rails 3.2</p>
<p>Das Ganze muss nun noch in die gewünschten Controller eingebaut werden. Man kann das Module einfach in den ApplicationsController einbinden und dann den Filter nur in den gewünschten Controllern aufrufen. In meinem Fall waren aber beide Controller Devise-Controller, die also nicht vom ApplicationController erben. Deswegen direkt z.B. im SessionsController:</p>
<pre class="brush: ruby; title: ; notranslate">
class SessionsController &lt; ::Devise::SessionsController
  include CookieDetection
  before_filter :cookies_required

  # ...
end
</pre>
<p>Der Filter funktioniert so: Beim Aufruf einer vom SessionsController generierten Seite (hier die Loginseite) wird die cookies_required-Methode aufgerufen. Diese prüft ob der Request einen Cookie enthält (dann sind wir fertig), kein GET-Request ist (der Cookie-Test basiert auf einem Redirect, Post-Daten gehen dabei verloren, deswegen macht das dafür nicht viel Sinn) oder wahrscheinlich von einem Bot kommt (die haben in der Regel keine Cookies aktiviert, sollen aber nicht die Warnung indexieren). Den Bot-Detection-Code habe ich samt des etwas exzentrischen Methoden-Namens <a href="http://gurge.com/blog/2007/01/08/turn-off-rails-sessions-for-robots/">von hier</a> geborgt und um einige aktuellere Bots erweitert.</p>
<p>Ist all dies nicht der Fall heißt das entweder, dass der Besucher die fragliche Seite als erste Seite seines Besuchs aufgerufen hat (sonst hätte er einen Session-Cookie &#8211; vielleicht kommt er über ein Bookmark?) oder dass sein Browser keine Cookies akzeptiert. Um diese beiden Fälle unterscheiden zu können, wird ein spezieller Cookie gesetzt und dann auf die aktuelle Adresse redirected. Dabei wird der aktuellen Adresse ein Parameter cookie_test=Timestamp angefügt, damit wir nicht in einer Endlosschleife landen.</p>
<p>Der Browser des Besuchers empfängt den Redirect samt Cookie. Hat der Browser Cookies aktiviert, ruft er die Seite erneut auf und sendet dabei den Cookie mit. Dann sind wir fertig. Hat der Browser keine Cookies aktiviert, landen wir wieder in cookies_required. Hier wird im Mittelteil auf den cookie_test-Parameter geprüft. Ist er vorhanden, wird eine Flash-Message gesetzt und dann abgebrochen.</p>
<p>Der Nutzer sieht damit beim ersten Aufruf der Login-Seite die Warnung, dass er Cookies akzeptieren muss, um die Seite zu benutzen. Das ist vor allem auch bei der Registrierung wichtig, wo man sich ärgern würde, wenn man viele Daten eingegeben hat und dann erst erfährt, dass die Registrierung mit dem aktuellen Browser so gar nicht geht (im IE ist es ja teilweise ein ganz schöner Krampf, das umzustellen). Da wir POST-Requests von der Prüfung ausgenommen haben und den Fehler auch nicht auf einer eigenen Seite anzeigen, kann der Nutzer nun natürlich trotzdem das Formular ausfüllen und abschicken. Dann hat er aber Pech und wird früher oder später wieder per Redirect am Ausgangsort landen, wo die gleiche Routine anspringt und ihm die Cookie-Warnung anzeigt.</p>
<p><img src="http://code.freudendahl.net/files/2012/04/cookie_detection.jpg" alt="Cookie-Warnmeldung" title="Cookie-Warnmeldung" width="500" height="72" class="aligncenter size-full wp-image-98 colorbox-97" /></p>
<p>Schön wäre es nun noch, das Ganze in einem Rspec/Capybara-Test grundlegend zu prüfen. Leider hat eine kurze Recherche ergeben, dass das wohl aktuell nicht ohne größere Verrenkungen geht. Wenn jemandem ein einfacher Weg einfällt, das zu machen, würde ich mich über einen Hinweis in den Kommentaren sehr freuen.</p>
<p>P.S.: Das Original-Cookie-Detection-Rezept macht zu Lizenzen des Codes keine Angaben. Von mir aus könnt ihr diese neue Version als Public Domain betrachten und sie nach Belieben verwenden.</p>
<p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></content:encoded>
			<wfw:commentRss>http://code.freudendahl.net/2012/04/ruby-on-rails-cookie-detection/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress-Plugin: InTypo (Bugfix)</title>
		<link>http://code.freudendahl.net/2012/04/wordpress-plugin-intypo-bugfix/</link>
		<comments>http://code.freudendahl.net/2012/04/wordpress-plugin-intypo-bugfix/#comments</comments>
		<pubDate>Sun, 22 Apr 2012 15:25:38 +0000</pubDate>
		<dc:creator>Johannes</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[WordPress-Plugin]]></category>

		<guid isPermaLink="false">http://code.freudendahl.net/?p=93</guid>
		<description><![CDATA[Ich verwende seit Jahren das Plugin <a href="http://dossier.dunker.de/intypo">InTypo</a>, weil WordPress selbst was Anführungszeichen betrifft leider total kaputt ist. Das könnte sich bald ändern, aber so richtig glaube ich es erst wenn ich es sehe. Bis dahin ist InTypo unverzichtbar, um korrekte deutsche Anführungszeichen zu habe. Leider scheint der Autor das Plugin momentan nicht mehr zu pflegen. Da im <a href="http://forum.wpde.org">WPDE-Forum</a> nachgefragt wurde, veröffentliche ich nun mal meine fehlerbereinigte Version des &#8230; <a href="http://code.freudendahl.net/2012/04/wordpress-plugin-intypo-bugfix/">Weiterlesen <span class="meta-nav">&#8594;</span></a><p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></description>
			<content:encoded><![CDATA[<p>Ich verwende seit Jahren das Plugin <a href="http://dossier.dunker.de/intypo">InTypo</a>, weil WordPress selbst was Anführungszeichen betrifft leider total kaputt ist. Das könnte sich bald ändern, aber so richtig glaube ich es erst wenn ich es sehe. Bis dahin ist InTypo unverzichtbar, um korrekte deutsche Anführungszeichen zu habe. Leider scheint der Autor das Plugin momentan nicht mehr zu pflegen. Da im <a href="http://forum.wpde.org">WPDE-Forum</a> nachgefragt wurde, veröffentliche ich nun mal meine fehlerbereinigte Version des Plugins.</p>
<p>@Darius Dunker: Falls Du das hier liest und angenommen, dass WordPress die Probleme, die InTypo behebt, noch länger mitschleppt, wäre es natürlich toll, die Bugfixes in einer offiziellen Version 0.9.3 zu sehen. Dann kommt der Download hier auch gerne wieder weg. <img src='http://code.freudendahl.net/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley colorbox-93' /> </p>
<p>Änderungen gegenüber 0.9.2:</p>
<ul>
<li>PHP-Änderungen um Notices zu verhindern (wurde mir mit WP_DEBUG auf true überall auf der Seite ausgegeben, was relativ nervig ist: &#8222;PHP Notice:  Undefined offset: 1 in [&#8230;]\wordpress\webseite\wp-content\plugins\intypo\intypo.php on line 36&#8243;)</li>
<li>PHP-Notice bei leerem WP_LANG verhindert</li>
<li>User Level durch Capabilities ersetzt (User Level sind schon ewig deprecated, mit den richtigen Einstellungen loggt WP dann auch ständig Warnungen dazu)</li>
<li>Bugfix für verschachtelte Tags (Ersetzung war kaputt für Schachtelungen von code und pre-Tags)</li>
<li>PHP-Notice beim ersten Speichern der Optionen behoben, Option wird nun immer automatisch geladen da eh auf jeder Seite nötig</li>
</ul>
<p>Je nach Einstellungen ist das alles nicht dramatisch, aber gerade wenn die Notices ausgegeben werden, macht einem das das Arbeiten mit WP halbwegs unmöglich (und als Entwickler will man ja Notices sehen, die eigenen unabsichtlich generierten nämlich). Der behobene Bug tritt nur auf, wenn man verschiedene Bereiche, in denen nicht ersetzt werden soll, ineinander schachtelt. Ich hatte z.B. in einem Code-Block ein pre-Tag enthalten, ab dem schließenden pre-Tag wurden dann plötzlich auch im Code-Block Anführungszeichen ersetzt, was für ein Blog wie dieses nicht gut ist.</p>
<p>Nicht angepasst habe ich, dass InTypo Anführungszeichen in Bild-Unterschriften nicht ersetzt, da diese per Shortcode gemacht sind. Das Betrifft aber auch jeden anderen Shortcode. Da geht nichts kaputt, die Anführungszeichen werden nur einfach nicht ersetzt. Das wäre Aufgabe des Codes, der den Shortcode bearbeitet, das richtig auszugeben, finde ich.</p>
<p>Die Bugfix-Version habe ich mal 0.9.2a genannt, um sie von der aktuellen Version 0.9.2 zu unterscheiden: <a href="http://code.freudendahl.net/files/2012/04/intypo_0.9.2a.zip">Download</a></p>
<p>Wenn ihr weitere Probleme mit InTypo beobachtet oder andere Bugs kennt, teilt mir das gerne mit und je nach Zeit schaue ich, ob ich da was machen kann. Solange wie WordPress selbst das nicht wasserdicht handhabt, ist InTypo nämlich auch nach all den Jahren eine gute Lösung. In diesem Sinne also auch vielen Dank an Darius für das Erstellen des Plugins!</p>
<p>P.S.: Es gibt auch noch andere Plugins wie <a href="http://wordpress.org/extend/plugins/wp-typography/">WP-Typography</a>. Ich hatte mir das Plugin nur einmal kurz angeschaut, fand es aber im Vergleich zu InTypo zu umfangreich. InTypo ist mit Kommentaren gerade mal 216 Zeilen lang. Es tut nur eine Sache, die aber sehr gut. Was aber nicht heißen soll, dass ich euch von WP-Typography oder anderen Lösungen abraten möchte. Für mich ist InTypo einfach die passende Lösung für das Problem.</p>
<p>P.P.S.: Das Original-Plugin macht keine Angaben zur Lizenz, also sollte es wohl unter GPL v2 stehen. Ich will mich da nicht festlegen und stelle meine Änderungen am Plugin erst mal unter GPL v2. Falls solche Spitzfindigkeiten jemanden interessieren *g* auch gerne unter andere Lizenzen, einfach mailen.</p>
<p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></content:encoded>
			<wfw:commentRss>http://code.freudendahl.net/2012/04/wordpress-plugin-intypo-bugfix/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Lokale WordPress-Installation</title>
		<link>http://code.freudendahl.net/2012/04/lokale-wordpress-installation/</link>
		<comments>http://code.freudendahl.net/2012/04/lokale-wordpress-installation/#comments</comments>
		<pubDate>Sun, 22 Apr 2012 14:23:08 +0000</pubDate>
		<dc:creator>Johannes</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Web-Development]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://code.freudendahl.net/?p=85</guid>
		<description><![CDATA[Oft möchte man an seiner WordPress-Seite etwas Neues ausprobieren, will aber den Betrieb der laufenden Seite nicht stören. Da macht sich dann eine lokale Installation sehr gut. Im folgenden will ich kurz beschreiben, was dazu nötig ist. Der Artikel bezieht sich auf Windows, unter anderen Systemen funktioniert es aber ähnlich. <br /><br /> Um eine lokale Kopie einer WordPress-Seite einzurichten, brauchen wir folgendes: PHP, MySQL und einen Webserver WordPress sowie &#8230; <a href="http://code.freudendahl.net/2012/04/lokale-wordpress-installation/">Weiterlesen <span class="meta-nav">&#8594;</span></a><p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></description>
			<content:encoded><![CDATA[<p>Oft möchte man an seiner WordPress-Seite etwas Neues ausprobieren, will aber den Betrieb der laufenden Seite nicht stören. Da macht sich dann eine lokale Installation sehr gut. Im folgenden will ich kurz beschreiben, was dazu nötig ist. Der Artikel bezieht sich auf Windows, unter anderen Systemen funktioniert es aber ähnlich.</p>
<h3>Bestandsaufnahme</h3>
<p>Um eine lokale Kopie einer WordPress-Seite einzurichten, brauchen wir folgendes:</p>
<ul>
<li>PHP, MySQL und einen Webserver</li>
<li>WordPress sowie alle Plugins und Themes</li>
<li>die Datenbank und die hochgeladenen Dateien</li>
<li>Scripte um diese lokale Installation mit wenig Handarbeit auf den aktuellen Stand zu bringen</li>
</ul>
<p>Insbesondere sollen auch Permalinks gehen und es sollten alle in Beiträgen enthaltenen Links noch funktionieren und nicht auf die Online-Version weiterleiten.</p>
<h3>Schritt 1: XAMPP einrichten</h3>
<p>Wie ihr PHP, Apache und MySQL auf euren Rechner bekommt, ist natürlich eure Sache. Da führen viele Wege zum Ziel, und eventuell nutzt ihr online ja nicht mal Apache sondern IIS oder NGINX. Für 9 von 10 Nutzern ist es aber sicher am einfachsten und passendsten, sich <a href="http://www.apachefriends.org/de/xampp.html">XAMPP</a> zu installieren. Ernsthaft, zumindest unter Windows würde ich das der einzelnen Installation der Komponenten immer vorziehen!</p>
<p>Bitte die Installationshinweise ernst nehmen und XAMPP nicht wie es ist ans Netz hängen! Der Server ist zum Entwickeln gedacht, nicht zum Online-Betrieb von Webseiten!</p>
<p>Ok, PHP, Apache und MySQL sind also da. Folgende Dateien solltet ihr kennen, am besten legt ihr euch an passender Stelle eine Verknüpfung an, die ihr leicht wiederfindet:</p>
<ul>
<li>C:\xampp\apache\conf\httpd.conf &#8211; Die ist die Konfigurationsdatei von Apache. Nach Änderungen Neustart nicht vergessen!</li>
<li>C:\xampp\php\php.ini &#8211; Dies ist die Konfigurationsdatei von PHP.</li>
<li>C:\xampp\apache\logs\error.log &#8211; Dies ist die Error-Logdatei des Apache. Fehler in der .htaccess werden hier z.B. geloggt.</li>
</ul>
<p>Zuerst könnte man jetzt die php.ini richtig konfigurieren, was ich hier jetzt nicht im Detail beschreiben will. Wichtig ist meiner Meinung nach, das Errorlogging richtig einzustellen. Am besten PHP-Fehler in eine eigene Datei loggen lassen und diese Datei dann auch an leicht wiederzufindender Stelle verknüpfen.</p>
<p>Wichtig ist ebenfalls, den Apache für WordPress anzupassen. Dazu die httpd.conf öffnen und nach &#8222;IfModule alias_module&#8220; suchen. In diesem Block das hier einfügen:</p>
<pre class="brush: plain; title: ; notranslate">  Alias /meine-seite &quot;D:/webapps/meine-seite-lokale-kopie&quot;
  &lt;Directory &quot;D:/webapps/meine-seite-lokale-kopie&quot;&gt;
    Options FollowSymLinks
    AllowOverride FileInfo
    Order allow,deny
    Allow from all
  &lt;/Directory&gt;</pre>
<p>Zur Erklärung: Normalerweise liefert XAMPP alles aus, was in C:\xampp\htdocs liegt. Das geht ohne weitere Konfiguration. Ich lege Projekte aber lieber auf D:\ ab, und das muss wie oben konfiguriert werden. Ihr sagt damit Apache, dass er den Aufruf http://localhost/meine-seite auf den Ordner D:/webapps/meine-seite-lokale-kopie mappen soll. Die beiden ersten Zeilen in dem Block sind wichtig, damit die Permalinks funktionieren. Lest jetzt am besten erst mal den Artikel zu Ende, Schritt 3 zeigt euch noch eine bessere Apache-Konfiguration, die obigen Schnipsel überflüssig macht.</p>
<h3>Schritt 2: WordPress einrichten</h3>
<p>Legt den eben konfigurierten Ordner an und kopiert alle Dateien der WordPress-Installation von eurem Webserver hinein. Dann braucht ihr natürlich noch eine Datenbank. Passt zuerst einmal die wp-config.php an. Datenbank-Server ist &#8222;localhost&#8220;, Name, Nutzer und Passwort könnt ihr so lassen oder ändern, wie ihr mögt. Die Datenbank müsst ihr lokal natürlich erst einmal anlegen. Dazu PhpMyAdmin oder ein Tool eurer Wahl öffnen und folgendes SQL ausführen (alle Daten natürlich entsprechend der wp-config.php ersetzen):</p>
<pre class="brush: sql; title: ; notranslate">CREATE DATABASE IF NOT EXISTS meineseite;
ALTER DATABASE meineseite CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON meineseite.* TO 'meinuser'@'localhost' IDENTIFIED BY 'meinpasswort';
FLUSH PRIVILEGES;</pre>
<h3>Schritt 3: Permalinks einrichten und Datenbank importieren</h3>
<p>Wir haben die Original-Datenbank noch nicht importiert, und das hat einen Grund. Die Datenbank enthält nämlich an mindestens zwei Stellen die Domain der Online-Version, was dazu führen würde, dass jeder Klick auf einen Link in der Seite uns zur Online-Version zurückführt. Das ist gefährlich, wenn man nicht aufpasst und im Glauben, auf der Testinstanz zu sein, an der Seite herumschraubt.</p>
<p>Die Adresse könnten wir jetzt einfach im Datenbank-Dump mit der localhost-Adresse ersetzen, das hat aber einen Nachteil: Manchmal steht die Adresse auch in serialisierten Strings, in denen die Länge mit codiert ist. Das geht kaputt wenn man einen String mit anderer Länge hineinschreibt. Ich habe deswegen mit einem kleinen Trick eine lokale Domain eingerichtet, unter der die lokale Seite aufgerufen wird. Die <a href="http://de.wikipedia.org/wiki/Hosts-Datei">HOSTS-Datei</a> öffnen (bei mir unter C:\Windows\System32\drivers\etc) und das hier ergänzen:</p>
<pre class="brush: plain; title: ; notranslate">127.0.0.1    meine-seite.loc             # local version</pre>
<p>Ich habe eine net-Domain, deswegen habe ich als lokales Kürzel &#8222;loc&#8220; gewählt. Für &#8222;de&#8220; müsstet ihr natürlich was anderes nehmen, z.B. &#8222;lc&#8220;. Hauptsache es gibt das nicht schon als echte Top-Level-Domain. Für Multisite-Installationen müsst ihr das analog auch für alle Sub-Domains machen. Ich würde dann für jede weitere Domain von 127.0.0.1 hochzählen (127.0.0.2 etc.).</p>
<p>Um über diese neue Domain die lokale Test-Installation aufrufen zu können, müsst ihr noch mal an die httpd.conf des Apache ran. Einfach das hier ganz unten einfügen (nur einmal pro Haupt-Domain, die Sub-Domains sind davon mit erfasst):</p>
<pre class="brush: plain; title: ; notranslate">
# meine-seite.net
&lt;VirtualHost 127.0.0.1&gt;
  ServerName meine-seite.loc
  ServerAlias *.meine-seite.loc
  DocumentRoot &quot;D:/webapps/meine-seite-lokale-kopie&quot;
  &lt;Directory &quot;D:/webapps/meine-seite-lokale-kopie&quot;&gt;
    Options FollowSymLinks
    AllowOverride FileInfo
    Order allow,deny
    Allow from all
  &lt;/Directory&gt;
&lt;/VirtualHost&gt;
</pre>
<p>Wenn ihr das so einrichtet, könnt ihr euch die Konfiguration aus Schritt 1 auch sparen. Ich habe das nur mit aufgenommen, falls jemandem die lokale Domain zu viel Aufwand ist. Siehe auch das Update am Ende des Textes!</p>
<p>Dann einfach einen Datenbank-Dump von der Online-Version ziehen, z.B. per PhpMyAdmin oder über ein Backup-Plugin für WordPress, und in diesem Dump global die Original-Domain mit der &#8222;Fake-Domain&#8220; ersetzen. Das ganze dann in die vorhin angelegte lokale Datenbank importieren. Für den Moment könnt ihr das manuell über PhpMyAdmin machen, Automatisierung kommt im nächsten Schritt.</p>
<p>So, an diesem Punkt sollte eine exakte Kopie eurer Webseite unter http://meine-seite.loc erreichbar sein. Geht als erstes auf Backend &gt; Einstellungen &gt; Permalinks und speichert die Permalinkstruktur neu ab. Das generiert die .htaccess-Datei neu, danach gehen auch alle Permalinks auf der Seite.</p>
<h3>Schritt 4: Automatisierung</h3>
<p>Wenn ihr eine Weile mit der Testinstanz gearbeitet habt, füllt sie sich mit Testinhalten, dafür fehlen die neueren echten Inhalte. Irgendwann kommt der Bedarf auf, die Testinstanz mit der Online-Version zu synchronisieren, um mit aktuellen Inhalten testen zu können. Das kann man natürlich wie oben beschrieben immer manuell machen, aber auf Dauer ist das nervig. Dafür habe ich mir ein kleines Script geschrieben.</p>
<p>Das Script selbst ist eine Batch-Datei, die nichts weiter tut als ein PHP-Script aufzurufen. Das ganze könnte man sicher besser mit Perl oder anderen Script-Sprachen lösen, und dann vor allem als eine Datei statt zwei umsetzen. Unter Windows war das aber die einfachste Lösung, ohne erst irgendwas installieren zu müssen. Das ganze ist nicht wirklich schön, aber es musste schnell gehen und funktioniert. <img src='http://code.freudendahl.net/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley colorbox-85' /> </p>
<p>Legt euch ein Verzeichnis für Scripts an und speichert das hier als import_dump.bat ab (Pfade ggf. anpassen):</p>
<pre class="brush: plain; title: ; notranslate">@echo off
C:\xampp\php\php.exe import_dump.php
</pre>
<p>Und das hier als import_dump.php:</p>
<pre class="brush: php; title: ; notranslate">&lt;?php

// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define('DB_NAME', 'meineseite');

/** MySQL database username */
define('DB_USER', 'meinuser');

/** MySQL database password */
define('DB_PASSWORD', 'meinpasswort');

// from: http://stackoverflow.com/questions/2159059/string-replace-in-a-large-file-with-php
function replace_file($path, $string, $replace) {
  set_time_limit(0);
  $result = 0;
  if (is_file($path) === true) {
    $file = fopen($path, 'r');
    $temp = tempnam(dirname(__FILE__), 'tmp');
    if (is_resource($file) === true) {
      while (feof($file) === false) {
        file_put_contents($temp, str_replace($string, $replace, fgets($file)), FILE_APPEND);
      }
      fclose($file);
    }
    unlink($path);
    $result = rename($temp, $path);
  }
  return $result;
}

// find the database file
$files = glob(dirname(__FILE__) . '/db*.sql');
if (sizeof($files) == 0) {
  echo 'no SQL file found';
  return;
}
$file = str_replace('/', '\\', $files[0]);

// loop through the file contents and change stuff
$result = replace_file($file, array(
  'meine-seite.net',
  's:83:&quot;/kunden/123456789/webseiten/meine-seite.net/wordpress/wp-content/blogs.dir/1/files/&quot;',
  'ping_sites\', \'http://rpc.pingomatic.com/',
  '&quot;cronjob_alert&quot;;i:1', // AntiVirus cron-job
  'webmaster@meine-seite.loc',
  'johannes@meine-seite.loc',
), array(
  'meine-seite.loc',
  's:64:&quot;D:/webapps/meine-seite-lokale-kopie/wp-content/blogs.dir/1/files/&quot;',
  'ping_sites\', \'',
  '&quot;cronjob_alert&quot;;i:0',
  'webmaster@meine-seite.net',
  'johannes@meine-seite.net',
));
if ($result == 0) {
  echo 'result of search-and-replace: 0' . &quot;\n&quot;;
}

// import dump into database
passthru('C:\xampp\mysql\bin\mysql.exe -u' . DB_USER . ' -p' . DB_PASSWORD . ' --default_character_set utf8 ' . DB_NAME . ' &lt; ' . $file, $result);
echo 'result of db import: ' . $result;
</pre>
<p>Ganz oben sind der Einfachheit halber die Datenbank-Daten aus der wp-config.php eingefügt. Das Script erwartet einen Datenbank-Dump im gleichen Verzeichnis, der dem Namensschema &#8222;db*.sql&#8220; folgt. Es wird die erste passende Datei genommen, also am besten die Datei nach dem Import löschen.</p>
<p>Gegen Ende des Scriptes ist aufgelistet, was mit was ersetzt wird. Hier müsst ihr natürlich eure Daten eintragen. Da wäre in erster Linie eure Domain zu ersetzen. Da an einigen Stellen Dateipfade serialisiert im Dump stehen, ersetze ich die auch. Hier müsstet ihr im Dump einfach mal nachschauen, wie das bei euch heißt (&#8222;/kunden/123456789/webseiten/&#8220; ist die Ordnerstruktur bei <a href="http://df.eu">Domainfactory</a>). Am Ende ersetze ich meine E-Mail-Adressen übrigens wieder zur Originalversion zurück, weil es mich mehrmals sehr verwirrt hat, dass der Gravatar für die .loc-Version nicht angezeigt wurde (geht ja auch schlecht).</p>
<p>Also: Script-Dateien speichern, Datenbank-Dump mit dem richtigen Namen in das gleiche Verzeichnis legen und das Script starten. Dann sollte eure Test-Datenbank durch den bereinigten Inhalt des DB-Dumps ersetzt werden. Alles, was ihr an der Datenbank der Testinstanz gemacht habt, ist dann natürlich weg!</p>
<h3>Schritt 5: Versionskontrolle</h3>
<p>Der letzte Schritt ist optional, aber wenn ihr längerfristig mit der Testinstanz arbeiten möchtet, macht es sich vielleicht gut, das ganze Projekt unter Versionskontrolle zu stellen. Das ist auch schon praktisch, wenn ihr alleine daran arbeitet. Wenn ihr z.B. gezwungen seid, ein Plugin zu patchen und nach einem Jahr gibt es da doch mal ein Upgrade, dann macht es sich super, wenn man im Logfile nachschlagen kann, ob und wenn ja was man geändert hatte.</p>
<p>Je nach Betriebssystem und euren sonstigen Ressourcen könnt ihr da wählen, was ihr möchtet (SVN, Git, Mercurial&#8230;). Ich habe mich für Git und den Hoster <a href="http://bitbucket.org">BitBucket</a> entschieden. GitHub ist im OpenSource-Bereich verbreiteter, bietet aber nur 5 private Repositories an. Bei BitBucket könnt ihr dagegen unbeschränkt viele private Projekt ablegen (auf die dann also niemand sonst Zugriff hat). Natürlich könnt ihr das ganze auch einfach nur auf eurem lokalen Rechner unter Versionskontrolle stellen, aber wenn die Daten extern gespiegelt sind, sind sie auch von überall aus verfügbar und dienen im Notfall als Backup (angepasstes Theme, geänderte Plugins etc.).</p>
<p>Ich habe mir also <a href="http://msysgit.github.com/">Git für Windows</a> installiert und im Root-Verzeichnis des Projekts ein neues Repository erzeugt. Nach der gut verständlichen Anleitung von BitBucket habe ich das dann mit BitBucket synchronisiert. Der Windows-Git-Client ist sehr spartanisch, aber um Änderungen zu committen und ab und an zu BitBucket zu pushen reicht es aus. </p>
<h3>Fazit</h3>
<p>Mit oben vorgestellten Maßnahmen haben wir nun eine lokale Kopie der Seite. Wenn es nun daran geht, ein neues Plugin zu integrieren oder ein WordPress-Update durchzuführen, kann man das ganz in Ruhe lokal ausprobieren. Erst wenn alles funktioniert, werden die Änderungen auch online eingespielt/durchgeführt. Das ganze System ist noch nicht perfekt, weil man z.B. Einstellungen online manuell wiederholen muss. Es ist aber allemal besser, als an einer Live-Seite rumzuschrauben.</p>
<p>Ich hoffe, der Artikel ist verständlich und hilft dem ein oder anderen weiter. Falls ich an bestimmten Stellen zu schnell war, einfach kommentieren und ich gehe mehr ins Detail.</p>
<p><strong>Update Mai 2012:</strong> Gerade merke ich, dass man nicht ohne weiteres mehrere VirtualHosts für 127.0.0.1 definieren kann. Man landet dann immer beim zuerst definierten. Dazu sollte man mal im Unterordner C:\xampp\apache\conf\extra die Datei httpd-vhosts.conf öffnen. Zum einen sollten hier eigentlich die VirtualHosts rein anstatt direkt in die httpd.conf, zum anderen ist hier die Direktive &#8222;NameVirtualHost *:80&#8243; einzukommentieren (&#8222;#&#8220; entfernen davor). Dann sollten auch mehrere VirtualHosts mit der gleichen IP, aber verschiedenen Namen klappen.</p>
<p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></content:encoded>
			<wfw:commentRss>http://code.freudendahl.net/2012/04/lokale-wordpress-installation/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress-MultiSite: Ein Theme für mehrere Blogs einsetzen</title>
		<link>http://code.freudendahl.net/2012/03/wordpress-multisite-ein-theme-fuer-mehrere-blogs-einsetzen/</link>
		<comments>http://code.freudendahl.net/2012/03/wordpress-multisite-ein-theme-fuer-mehrere-blogs-einsetzen/#comments</comments>
		<pubDate>Sat, 03 Mar 2012 18:49:51 +0000</pubDate>
		<dc:creator>Johannes</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[MultiSite]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://code.freudendahl.net/?p=80</guid>
		<description><![CDATA[Diese und alle anderen freudendahl.net-Seiten sind mit einer WordPress-MultiSite-Installation umgesetzt. Das bedeutet, dass ich mehrere voneinander getrennte Webseiten/Blogs mit der gleichen WP-Installation betreibe, was eine tolle Sache ist und viel Arbeit spart. Im folgenden will ich kurz erklären, wie man ein Theme für mehrere Blogs einsetzt und wie man es trotzdem für einzelne Blogs individuell anpassen kann. Natürlich kann jedes Blog auf einer MultiSite-Installation ein eigenes Theme kriegen. Es kann &#8230; <a href="http://code.freudendahl.net/2012/03/wordpress-multisite-ein-theme-fuer-mehrere-blogs-einsetzen/">Weiterlesen <span class="meta-nav">&#8594;</span></a><p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></description>
			<content:encoded><![CDATA[<p>Diese und alle anderen freudendahl.net-Seiten sind mit einer WordPress-MultiSite-Installation umgesetzt. Das bedeutet, dass ich mehrere voneinander getrennte Webseiten/Blogs mit der gleichen WP-Installation betreibe, was eine tolle Sache ist und viel Arbeit spart. Im folgenden will ich kurz erklären, wie man ein Theme für mehrere Blogs einsetzt und wie man es trotzdem für einzelne Blogs individuell anpassen kann.</p>
<p>Natürlich kann jedes Blog auf einer MultiSite-Installation ein eigenes Theme kriegen. Es kann sich aber durchaus auch lohnen, alle oder zumindest mehrere Blogs mit dem gleichen Theme zu betreiben. Anpassungen an dem Theme stehen so z.B. gleich auf allen Seiten zur Verfügung, und oft sind die Unterschiede zwischen den Blogs sowieso gering und Einstellungssache (Headerbild, Position der Sidebar). Um das Theme für einzelne Blogs anzupassen, gibt es prinzipiell zwei Wege: Per Stylesheet (CSS) oder in den Themedateien (PHP). In beiden Fällen müssen wir bei einem Aufruf der Seite zuerst ermitteln, zu welchem Blog der aktuelle Aufruf gehört.</p>
<h3>Anpassungen in Themedateien</h3>
<p>Anpassungen in Themedateien können z.B. das Ausblenden oder Verschieben von Metainformationen oder das Hinzufügen von Widget-Bereichen (Sidebars) sein. In jedem Fall muss in der Themedatei bekannt sein, um welches Blog es gerade geht. Dafür kann man auf die Informationen aus der wp_blogs-Tabelle zugreifen. Darin steht pro Blog eine Domain und eine Pfad-Angabe. Wenn ihr MultiSite mit Domains oder Subdomains verwendet, dann könnt ihr die folgenden Beispiele so übernehmen. Für Unterordner-Installationen solltet ihr die Beispiele von domain zu path ändern. Alle folgenden Code-Beispiele können in die functions.php eures Themes.</p>
<pre class="brush: php; title: ; notranslate">/**
 * Checks whether the current blog is the code blog.
 *
 * @return bool True if the current blog is the code blog site, false
 * otherwise.
 */
function abc_is_code_blog() {
  global $blog_id;
  $blog_details = get_blog_details($blog_id, false);
  return (strpos($blog_details-&gt;domain, 'code.example') === 0);
}</pre>
<p>Da ich eine sehr geringe Anzahl Blogs betreibe habe ich mir pro Blog eine Funktion wie die obige angelegt. Also abc_is_code_blog(), abc_is_main_blog()&#8230; Damit kann ich dann ganz simpel in Themedateien Änderungen per if (&#8230;) bzw. if (!&#8230;) nur für einzelne Blogs durchführen bzw. nicht durchführen. In der Funktion müsst ihr &#8222;code.example&#8220; durch eure Subdomain ersetzen! Der Positions-Vergleich anstatt eines direkten String-Vergleichs findet übrigens statt, da ich eine Entwicklungskopie der Blogs lokal als &#8222;freudendahl.loc&#8220; betreibe und der gleiche Test da natürlich auch funktionieren soll.</p>
<h3>Anpassungen im Stylesheet</h3>
<p>Die Styles pro Blog abzuändern ist keine große Kunst &#8211; wenn man eine Klasse hat, an der an diese Änderungen aufhängen kann. Um alle Seitenbereiche erfassen zu können, muss diese Klasse sinnvollerweise am body-Tag hängen. Manch ein Theme mag das schon von sich aus ergänzen (nachschauen schadet nicht), aber Twenty Eleven, was hier modifiziert als Child-Theme zum Einsatz kommt, bietet das leider nicht an.</p>
<p><img src="http://code.freudendahl.net/files/2012/03/blog-class.jpg" alt="Screenshot body-Tag" title="neue Klasse am body-Tag" width="500" height="211" class="aligncenter size-full wp-image-83 colorbox-80" /></p>
<p>Die im Screenshot zu sehende Klasse können wir wie folgt ganz leicht ergänzen:</p>
<pre class="brush: php; title: ; notranslate">
/**
 * Adds a site-specific CSS class to the list of body classes.
 *
 * @param array $classes An array of body class names.
 * @return array The modified body class name array.
 */
function abc_body_class($classes) {
  global $blog_id;

  $blog_details = get_blog_details($blog_id, false);
  $domain = str_replace('.', '-', $blog_details-&gt;domain);
  $classes[] = 'blogclass-' . $domain;
  return $classes;
}

add_filter('body_class', 'abc_body_class');
</pre>
<p>Die Methode &#8222;abc_body_class&#8220; wird über den &#8222;body_class&#8220;-Filter aufgerufen. Dieser Filter ist schon ein Weilchen Teil von WordPress, aber nicht jedes Theme unterstützt ihn. Wenn das wie oben nicht klappt, schaut mal in die header.php eures Themes. Das sollte dort so aussehen:</p>
<pre class="brush: php; title: ; notranslate">&lt;body &lt;?php body_class(); ?&gt;&gt;</pre>
<p>Damit sollte am body-Tag dann eine Klasse wie &#8222;blogclass-example-org&#8220; zur Verfügung stehen (in meinem Fall &#8222;blogclass-freudendahl-net&#8220;, &#8222;blogclass-code-freudendahl-net&#8220; und &#8222;blogclass-johannes-freudendahl-net&#8220;). Mit dieser Klasse sollte man ohne Probleme Definitionen im Stylesheet nur für bestimmte Blogs treffen können:</p>
<pre class="brush: css; title: ; notranslate">
a {
  color: red;
}

#blogclass-example-org a {
  color: blue;
}
</pre>
<p>Immer unter der allgemeingültigen Definition (diese soll ja überschrieben werden) oder vielleicht auch gesammelt ganz am Ende des Stylesheets.</p>
<p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></content:encoded>
			<wfw:commentRss>http://code.freudendahl.net/2012/03/wordpress-multisite-ein-theme-fuer-mehrere-blogs-einsetzen/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Quotes Collection optimieren</title>
		<link>http://code.freudendahl.net/2012/02/quotes-collection-optimieren/</link>
		<comments>http://code.freudendahl.net/2012/02/quotes-collection-optimieren/#comments</comments>
		<pubDate>Sun, 26 Feb 2012 22:48:48 +0000</pubDate>
		<dc:creator>Johannes</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[WordPress-Plugin]]></category>

		<guid isPermaLink="false">http://code.freudendahl.net/?p=78</guid>
		<description><![CDATA[Ich verwende seit kurzem das WordPress-Plugin <a href="http://wordpress.org/extend/plugins/quotes-collection/">Quotes Collection</a> zum Anzeigen von Zufallszitaten. Das Plugin ist auf den ersten Blick gut geschrieben und bietet relativ genau das, was ich brauche. Auf den zweiten Blick bietet es allerdings wesentlich mehr als ich brauche, und die entsprechenden Sachen habe ich nun mal wegoptimiert. Alle folgenden Code-Beispiele kommen in euer <a href="http://code.freudendahl.net/2012/02/wordpress-klassenbasiertes-funktions-plugin/">Funktions-Plugin</a> oder zur Not in die functions.php des Themes (wo es aber &#8230; <a href="http://code.freudendahl.net/2012/02/quotes-collection-optimieren/">Weiterlesen <span class="meta-nav">&#8594;</span></a><p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></description>
			<content:encoded><![CDATA[<p><a href="http://code.freudendahl.net/files/2012/02/quotes.jpg"><img src="http://code.freudendahl.net/files/2012/02/quotes.jpg" alt="Zufallszitat" title="Zufallszitat" width="250" height="215" class="alignright size-full wp-image-79 colorbox-78" /></a>Ich verwende seit kurzem das WordPress-Plugin <a href="http://wordpress.org/extend/plugins/quotes-collection/">Quotes Collection</a> zum Anzeigen von Zufallszitaten. Das Plugin ist auf den ersten Blick gut geschrieben und bietet relativ genau das, was ich brauche. Auf den zweiten Blick bietet es allerdings wesentlich mehr als ich brauche, und die entsprechenden Sachen habe ich nun mal wegoptimiert.</p>
<p>Alle folgenden Code-Beispiele kommen in euer <a href="http://code.freudendahl.net/2012/02/wordpress-klassenbasiertes-funktions-plugin/">Funktions-Plugin</a> oder zur Not in die functions.php des Themes (wo es aber eher nicht hingehört, da es sich nicht um Theme-Anpassungen handelt):</p>
<pre class="brush: php; title: ; notranslate">
// remove quote collection's CSS and JS
add_action('init', 'abc_remove_quotescollection_stuff', 12);

/**
 * Removes Quote Collection's Javascript and CSS files as they are not
 * needed. Also some of the hooks and filters of the plugin.
 */
function abc_remove_quotescollection_stuff() {
  // remove stuff here
}
</pre>
<p>Die &#8218;12&#8242; ist wichtig, damit diese Funktion nach den Funktionen des Plugins läuft. Andernfalls kann ja nichts entfernt werden.</p>
<p>Bevor ich zu Details komme eine Bitte: Versteht bitte wirklich, was die folgenden Code-Zeilen tun! Es wird hiermit absichtlich ein Teil der Funktionalität des Plugins ausgeschaltet. Bitte also nicht einfach blind kopieren und sich dann wundern, warum einiges nicht mehr geht. <img src='http://code.freudendahl.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley colorbox-78' /> </p>
<pre class="brush: php; title: ; notranslate">
wp_dequeue_script('quotescollection');
</pre>
<p>Ich nutze von &#8222;Quotes Collection&#8220; ausschließlich das Widget, und zwar ohne den Link, mit dem man sich per AJAX durch die Zitate durchklicken kann. Für diese AJAX-Aufrufe bindet das Plugin JavaScript-Code im Seitenheader ein, den ich aber gar nicht brauche. Das Plugin bindet den Code übrigens auf allen Seiten ein, ungeachtet der im Widget gewählten Optionen, da der gleiche AJAX-Link auch an Zitaten stehen kann, die man per Shortcode oder Template-Funktion eingebaut hat. Das Plugin weiß also nicht, ob der Link vielleicht irgendwo auf der Seite steht, deshalb gibt es immer den JavaScript-Code aus.</p>
<pre class="brush: php; title: ; notranslate">remove_action('wp_head', 'quotescollection_css_head');</pre>
<p>Das Plugin bindet auch auf allen Seiten ein Stylesheet ein, das aber nur wenige Zeilen lang ist. Da man ja möglichst wenige CSS-Dateien referenzieren soll, um die Zahl der HTTP-Requests klein zu halten, habe ich dieses Stylesheet ebenfalls entfernt und die wichtigen Zeilen ins Stylesheet meines Themes übernommen.</p>
<pre class="brush: php; title: ; notranslate">remove_filter('the_content', 'quotescollection_inpost', 7);
remove_filter('the_excerpt', 'quotescollection_inpost', 7);</pre>
<p>Diese beiden Zeilen entfernen eine Shortcode-ähnliche Funktion (wieso ist das nicht als Shortcode umgesetzt?!), welche einen Shortcode in Beiträgen und Auszügen durch ein oder mehrere Zitate ersetzt. Da ich das nicht verwende kann ich mir die Regular Expressions sparen, die bei jedem Aufruf jeden Beitrags ausgeführt werden.</p>
<pre class="brush: php; title: ; notranslate">remove_shortcode('quotescollection', 'quotescollection_shortcodes');
remove_shortcode('quotcoll', 'quotescollection_shortcodes');
remove_shortcode('quotecoll', 'quotescollection_shortcodes');</pre>
<p>Und schlußendlich hat das Plugin auch noch einige echte Shortcodes an Bord, über die man Zitate in Beiträge oder Seiten einbetten kann. Auch dies hat eine Regular Expression pro Shortcode und Beitrag zur Folge, die ich mir sparen kann.</p>
<p>So, und damit ist das Plugin gleich wesentlich schlanker und verbraucht ein klein wenig weniger Ressourcen. Das Widget im Footer der Seite funktioniert natürlich weiter wie gewünscht, genau wie das Backend.</p>
<p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></content:encoded>
			<wfw:commentRss>http://code.freudendahl.net/2012/02/quotes-collection-optimieren/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress: Verschiedene Längen für die Kurzfassung</title>
		<link>http://code.freudendahl.net/2012/02/wordpress-verschiedene-laengen-fuer-die-kurzfassung/</link>
		<comments>http://code.freudendahl.net/2012/02/wordpress-verschiedene-laengen-fuer-die-kurzfassung/#comments</comments>
		<pubDate>Sun, 12 Feb 2012 23:56:57 +0000</pubDate>
		<dc:creator>Johannes</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Twenty Eleven]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://code.freudendahl.net/?p=52</guid>
		<description><![CDATA[Eines der schönen Features an WordPress ist, dass es aus einem längeren Text automatisch eine Kurzfassung generieren kann (im Englischen &#8222;excerpt&#8220; genannt). Die Kurzfassung wird normalerweise auf Übersichtsseiten wie der Startseite, der Suchergebnis-Seite oder Kategorie-Archiven angezeigt. In den WordPress-Einstellungen kann man zudem festlegen, ob im Feed der ganze Beitrag oder ebenfalls die Kurzfassung angezeigt werden soll. Mehr kann man zur Kurzfassung leider nicht einstellen: WordPress nimmt immer die ersten 55 &#8230; <a href="http://code.freudendahl.net/2012/02/wordpress-verschiedene-laengen-fuer-die-kurzfassung/">Weiterlesen <span class="meta-nav">&#8594;</span></a><p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></description>
			<content:encoded><![CDATA[<p>Eines der schönen Features an WordPress ist, dass es aus einem längeren Text automatisch eine Kurzfassung generieren kann (im Englischen &#8222;excerpt&#8220; genannt). Die Kurzfassung wird normalerweise auf Übersichtsseiten wie der Startseite, der Suchergebnis-Seite oder Kategorie-Archiven angezeigt. In den WordPress-Einstellungen kann man zudem festlegen, ob im Feed der ganze Beitrag oder ebenfalls die Kurzfassung angezeigt werden soll. Mehr kann man zur Kurzfassung leider nicht einstellen: WordPress nimmt immer die ersten 55 Wörter des Beitrags. Im Folgenden soll es um Möglichkeiten gehen, die Länge dieser Kurzfassung anzupassen.</p>
<p>Zuerst ein Hinweis: Alle folgenden Code-Schnipsel gehören in die functions.php eures Themes. Wenn ihr ein bekanntes Theme wie Twenty Eleven verwendet, bei dem mit Updates zu rechnen ist, legt lieber ein <a href="http://bueltge.de/wordpress-child-themes-verstehen/1192/">Child-Theme</a> an, ehe ihr das Original ändert. Änderungen, die sich nicht direkt auf das Theme beziehen (im Prinzip alles außer dem Twenty-Eleven-Ansatz), könnt ihr auch in ein <a href="http://code.freudendahl.net/2012/02/wordpress-klassenbasiertes-funktions-plugin/">Funktions-Plugin</a> packen. Alle Codes auf dieser Seite wurden mit WordPress 3.3 getestet, funktionieren aber auch mit älteren und neueren WP-Versionen. Das Funktionspräfix &#8222;abc_&#8220; bitte durch ein Präfix eurer Wahl ersetzen um Kollisionen mit den Namen existierender Funktionen zu vermeiden!</p>
<h3>Der Normalfall</h3>
<p>Im Normalfall passt man die Länge der automatischen Kurzfassung so an:</p>
<pre class="brush: php; title: ; notranslate">/**
 * Sets the post excerpt length to 42 words.
 *
 * @param int $length The current excerpt length.
 * @return int The new excerpt length in words.
 */
function abc_excerpt_length($length) {
  return 42;
}

add_filter('excerpt_length', 'abc_excerpt_length');</pre>
<p>WordPress bietet zum Ermitteln der Länge des Excerpts einen Filter an. Der Standard-Wert von 55 wird als Parameter übergeben, kann von der sehr simplen Funktion aber ignoriert werden. Sie gibt einfach den neuen Wert zurück, hier 42.</p>
<h3>Twenty Ten und Twenty Eleven</h3>
<p>Die beiden Standard-Themes Twenty Ten und Twenty Eleven sind etwas speziell: Sie filtern den Excerpt-Wert selber, um Child-Themes ein leichteres Überschreiben zu ermöglichen. Das führt leider dazu, dass obiger Code keinen Effekt hat. Hier muss man den alten Filter erst entfernen und dann selber einen Wert setzen:</p>
<pre class="brush: php; title: ; notranslate">/**
 * Sets the post excerpt length to 42 words.
 *
 * @param int $length The current excerpt length.
 * @return int The new excerpt length in words.
 */
function abc_excerpt_length($length) {
  return 42;
}

remove_filter('excerpt_length', 'twentyeleven_excerpt_length');
add_filter('excerpt_length', 'abc_excerpt_length');</pre>
<p>Das funktioniert soweit, vor unserer eigenen Funktion wird aber immer noch die Twenty-Eleven-Funktion aufgerufen (für Twenty Ten heißt der zu entfernende Filter natürlich &#8222;twentyten_excerpt_length&#8220;). Das macht an sich  nichts, aber wenn man sich ein paar sinnlose Funktionsaufrufe sparen will, kann man den remove_filter-Aufruf in die setup-Funktion des Child-Themes verlagern. Der Filter wird dann entfernt nachdem das Parent-Theme ihn hinzugefügt hat. Falls es irgendwelche Probleme gibt, dass nach dem eigenen Filter noch ein anderer Filter den Wert wieder ändert, kann man add_filter als dritten Parameter eine Zahl &gt; 10 mitgeben, z.B. 100. Das sollte garantieren, dass der eigene Wert nicht überschrieben wird.</p>
<h3>Verschiedene Längen je nach Kontext</h3>
<p>Der Wert für die Länge der Kurzfassung eines Artikels wird nicht etwa in der Datenbank gespeichert. Vielmehr wird der Filter für die Länge der Kurzfassung tatsächlich jedes Mal aufgerufen, wenn eine Kurzfassung eines Artikels generiert wird. Das gibt uns interessante Möglichkeiten, für verschiedene Gelegenheiten verschiedene Längen zu generieren. Im Folgenden ein paar Beispiele. Mehr <a href="http://codex.wordpress.org/Conditional_Tags">Conditional Tags</a> finden sich im Codex.</p>
<pre class="brush: php; title: ; notranslate">/**
 * Sets the post excerpt length to 60 words, except for some pages.
 *
 * @param int $length The current excerpt length.
 * @return int The new excerpt length in words.
 */
function abc_excerpt_length($length) {
  // startpage
  if (is_home()) {
    return 5;
  }
  // search result page
  if (is_search()) {
    return 10;
  }
  // in the feed
  if (is_feed()) {
    return 20;
  }
  // on every other page
  return 60;
}

add_filter('excerpt_length', 'abc_excerpt_length');</pre>
<p>In obigem Beispiel werden unterschiedliche Längen für die Startseite, die Suchergebnisseite, den Feed und alle anderen Seiten verwendet. Dies kann man unter Verwendung oben verlinkter Conditional Tags so feingranular gestalten, wie man möchte. Übertreiben sollte man es nicht dabei, aber der Startseite oder dem Feed eine andere Länge für die Kurzfassung zu geben, mag durchaus Sinn machen.</p>
<p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></content:encoded>
			<wfw:commentRss>http://code.freudendahl.net/2012/02/wordpress-verschiedene-laengen-fuer-die-kurzfassung/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress: Klassenbasiertes Funktions-Plugin</title>
		<link>http://code.freudendahl.net/2012/02/wordpress-klassenbasiertes-funktions-plugin/</link>
		<comments>http://code.freudendahl.net/2012/02/wordpress-klassenbasiertes-funktions-plugin/#comments</comments>
		<pubDate>Fri, 10 Feb 2012 00:22:08 +0000</pubDate>
		<dc:creator>Johannes</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://code.freudendahl.net/?p=49</guid>
		<description><![CDATA[Im Web findet man an vielen Stellen Code-Schnipsel für WordPress, so ja auch in diesem Blog. Der Rat lautet dann allgemein, diese in die functions.php des Themes zu packen. Das macht allerdings gar nicht immer Sinn, es ist nur oft das einfachste, was auch unerfahrene User verstehen. Da aber auch der Umgang mit einem eigenen Plugin nicht wirklich schwierig ist, möchte ich das in Zukunft als bevorzugte Variante empfehlen und &#8230; <a href="http://code.freudendahl.net/2012/02/wordpress-klassenbasiertes-funktions-plugin/">Weiterlesen <span class="meta-nav">&#8594;</span></a><p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></description>
			<content:encoded><![CDATA[<p>Im Web findet man an vielen Stellen Code-Schnipsel für WordPress, so ja auch in diesem Blog. Der Rat lautet dann allgemein, diese in die functions.php des Themes zu packen. Das macht allerdings gar nicht immer Sinn, es ist nur oft das einfachste, was auch unerfahrene User verstehen. Da aber auch der Umgang mit einem eigenen Plugin nicht wirklich schwierig ist, möchte ich das in Zukunft als bevorzugte Variante empfehlen und dazu hier ein Beispiel posten, das jeder als Grundlage für ein eigenes Plugin nehmen kann.</p>
<p>Kurz zu den Hintergründen: Man sollte generell bei jedem Code-Schnipsel kurz überlegen, was er tut. Ändert er konkret einen Aspekt des Themes, dann ist er in der functions.php richtig aufgehoben, schließlich ist diese ja Teil des Themes. Oft will man aber auch generell etwas am Verhalten von WordPress ändern, was völlig unabhängig vom Theme ist. Dann gehört der Code eher in ein Plugin. Fragen sollte man sich bei der Entscheidung immer, ob man die Änderung so auch noch verwenden wollen würde, wenn man das Theme wechselt.</p>
<p>Ein eigenes Plugin hat zudem den Vorteil, das es bei Updates des Themes nicht überschrieben wird. Wenn man sich selber ein Custom Theme bastelt oder ein <a href="http://bueltge.de/wordpress-child-themes-verstehen/1192/">Child-Theme</a> nutzt, ist das natürlich kein Problem. Aber wer etwa das Standard-Theme Twenty Eleven ändert und dann ein halbes Jahr später im Stress ein angezeigtes Update für Twenty Eleven automatisch installiert, der hat hoffentlich ein Backup der Theme-Dateien angelegt. <img src='http://code.freudendahl.net/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley colorbox-49' /> </p>
<p>Ein eigenes Plugin also. Ist das nicht total schwierig, ein Plugin zu erstellen? Nein, es sind nur wenige Zeilen PHP-Code nötig. Prinzipiell reicht es, eine PHP-Datei mit einem eindeutigen Namen im wp-content/plugins-Ordner abzulegen und sie mit einem PHP-Kommentar in einem gewissen Format beginnen zu lassen. Man muss das Plugin dann natürlich auch aktivieren, sonst tut es nichts. Bei Multisite-Installationen sollte man sich überlegen, ob man das Plugin für alle Blogs aktiviert, nur auf einzelnen Blogs oder pro Blog ein eigenes Funktions-Plugin anlegt.</p>
<p>So, und damit ohne weitere Vorrede zum Plugin:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
/*
Plugin Name: My Functions
Description: Adds functionality that doesn't belong into a theme's function.php.
Version: 0.1
Author: You
License: GPL2 (or later)
*/

/*
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    as published by the Free Software Foundation; either version 2
    of the License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

/**
 * Define a class to encapsulate the functions.
 */
class MyFunctions {

  //--------------------------------------------------------------------------
  // constructor
  //--------------------------------------------------------------------------

  /**
   * Creates a MyFunctions instance and registers all necessary hooks and
   * filters.
   */
  public function __construct() {
    // example: write a comment what the hook is for
    //add_filter('filter_name', array(&amp;$this, 'function_name'));
  }

  //--------------------------------------------------------------------------
  // functions
  //--------------------------------------------------------------------------

  //public function function_name() {
  //
  //}

}

// instantiate the class to actually do something
new MyFunctions();
</pre>
<p>Hinweis: Das Plugin nutzt eine Klasse zur Kapselung der Funktionalität. Ihr braucht also PHP 5 dazu, aber es arbeitet sowieso niemand mehr ernsthaft mit PHP 4, richtig? <img src='http://code.freudendahl.net/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley colorbox-49' /> </p>
<p>Wie funktioniert das nun? Das Plugin definiert eine Klasse MyFunctions und legt darunter eine Instanz dieser Klasse an, damit der Code in der Klasse auch ausgeführt wird. Auf der Instanz werden keine Funktionen aufgerufen, aber der Konstruktor sorgt dafür, dass alle Funktionen an den richtigen Filter- oder Action-Hook gehängt werden. Der obige Code enthält ein auskommentiertes Beispiel für einen Filter und darunter die Methode, die bei diesem Filter aufgerufen wird.</p>
<p>Das Schöne an der Klasse: Man muss sich um Namenskollisionen keine Sorgen machen (außer bei der Klasse selber, ändert den Namen ruhig zu etwas individuellerem, dann aber auch bei der Instantiierung ganz unten). In der functions.php kann man z.B. nicht einfach eine Funktion namens wp_trim_excerpt anlegen, weil es diese Funktion in WordPress schon gibt. Das gibt gleich einen Fehler und ggf. eine weiße Seite statt der Website. Innerhalb der Klasse kann man sich namenstechnisch dagegen austoben, und ich würde sogar dazu raten, die Methoden einfach gleich so zu nennen wie die Filter/Actions, für die sie gedacht sind. Das macht das ganze etwas übersichtlicher.</p>
<p>Noch ein Hinweis: Die Filter/Action-Aufrufe im Konstruktor der Klasse müsst ihr in einem Punkt anpassen verglichen mit den allermeisten Code-Schnipseln, die man im Web findet. Da ihr eine Klasse nutzt, müsst ihr die Instanz mitgeben. Wenn ihr also irgendwo den Hinweis findet, eine Funktion so hier aufzurufen:</p>
<pre class="brush: php; title: ; notranslate">
add_filter('wp_trim_excerpt', 'mein_prefix_wp_trim_excerpt');
</pre>
<p>Dann müsste das für dieses Plugin so aussehen:</p>
<pre class="brush: php; title: ; notranslate">
add_filter('wp_trim_excerpt', array(&amp;$this, 'wp_trim_excerpt'));
</pre>
<p>Hier wird einfach mit &amp;$this die Instanz mitgegeben, und um das machen zu können muss beides noch in ein Array. Alles in allem machbar. Im Sinne robusterer WordPress-Seiten wäre es also schön, wenn in Zukunft mehr Code-Schnipsel in einem solchen Plugin landen würden &#8211; aber eben auch nur diejenigen, die dort reingehören.</p>
<p><strong>P.S.:</strong> Die Idee zu diesem Beitrag kam natürlich nicht einfach von irgendwo, über Plugins statt der functions.php wird in der Community schon länger geredet. Stellvertretend hier ein Link zu einem <a href="http://justintadlock.com/archives/2011/02/02/creating-a-custom-functions-plugin-for-end-users">Beitrag von Justin Tadlock</a>, der alle Argumente sehr schön zusammenfasst. Sein Beispiel-Plugin ist allerdings nicht klassenbasiert, und wenn dann wollte ich es schon richtig machen. WordPress enthält wirklich schon genug Funktionssuppe. <img src='http://code.freudendahl.net/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley colorbox-49' /> </p>
<p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></content:encoded>
			<wfw:commentRss>http://code.freudendahl.net/2012/02/wordpress-klassenbasiertes-funktions-plugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress-Admin anpassen: Fehlende Artikelbilder markieren</title>
		<link>http://code.freudendahl.net/2012/01/wordpress-admin-anpassen-fehlende-artikelbilder-markieren/</link>
		<comments>http://code.freudendahl.net/2012/01/wordpress-admin-anpassen-fehlende-artikelbilder-markieren/#comments</comments>
		<pubDate>Wed, 25 Jan 2012 21:47:21 +0000</pubDate>
		<dc:creator>Johannes</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://code.freudendahl.net/?p=39</guid>
		<description><![CDATA[In diesem Beitrag beschreibe ich, wie ihr im WordPress-Backend eine Warnung einblenden könnt, wenn ein Artikel kein Artikelbild hat. Seit einigen WordPress-Versionen gibt es das Feature, dass man ein hochgeladenes Bild als Artikelbild markieren kann (engl. post thumbnail). Das Theme kann dieses Bild dann gesondert darstellen, z.B. neben dem Auszug auf Übersichtsseiten (Startseite, Archiv, Suchergebnis), was den sonst eher knappen Auszug ordentlich aufwertet. Insbesondere nimmt WordPress hierfür automatisch eine verkleinerte &#8230; <a href="http://code.freudendahl.net/2012/01/wordpress-admin-anpassen-fehlende-artikelbilder-markieren/">Weiterlesen <span class="meta-nav">&#8594;</span></a><p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></description>
			<content:encoded><![CDATA[<p><img src="http://code.freudendahl.net/files/2012/01/artikelbild-300x300.jpg" alt="Als Artikelbild nutzen" width="300" height="300" class="alignright size-full wp-image-40 colorbox-39" />In diesem Beitrag beschreibe ich, wie ihr im WordPress-Backend eine Warnung einblenden könnt, wenn ein Artikel kein Artikelbild hat. Seit einigen WordPress-Versionen gibt es das Feature, dass man ein hochgeladenes Bild als Artikelbild markieren kann (engl. post thumbnail). Das Theme kann dieses Bild dann gesondert darstellen, z.B. neben dem Auszug auf Übersichtsseiten (Startseite, Archiv, Suchergebnis), was den sonst eher knappen Auszug ordentlich aufwertet. Insbesondere nimmt WordPress hierfür automatisch eine verkleinerte Version des Bildes. Früher musste man das manuell machen, was ganz schön nervte: Ersten Absatz des Artikels samt HTML-Code für die Bilder ins Auszug-Feld kopieren, Text anpassen, Bild-URL zur Thumbnail-Version anpassen&#8230; Woah, war das aufwändig. Heute klickt man nur noch im Upload-Dialog auf &#8222;als Artikelbild markieren&#8220; und fertig.</p>
<p>Das Problem dabei: Man kann es leicht vergessen, ein Artikelbild festzulegen. Insbesondere schreibe ich oft Beiträge und plane sie für die Zukunft. Der Vorschau-Link zeigt mir die Einzelansicht, welche ich natürlich kontrolliere, aber ich sehe dann nicht, wie der Artikel auf der Startseite aussieht. Wenn er später veröffentlicht wird, schaue ich noch mal im RSS-Feed, aber oft eben auch nicht auf eine Übersichtsseite wie die Startseite. Früher ist mir das dann oft viel später erst aufgefallen, dass der Auszug ohne Bild daherkommt. Für das aktuelle Redesign habe ich mir deswegen eine kleine Warnung gebaut, die in der Artikelliste im Backend ausgegeben wird, wenn ein Artikel kein Artikelbild hat. Das sieht dann z.B. so aus:</p>
<p><img src="http://code.freudendahl.net/files/2012/01/artikelbild2.jpg" alt="Artikelbild-Warnung" width="550" height="126" class="aligncenter size-full wp-image-41 colorbox-39" /></p>
<p>Das Icon wird vor dem Titel eingeblendet, und zwar für alle Artikel, die mindestens ein Bild haben, aber kein Artikelbild. Manche Beiträge kommen ja nun mal ohne Bild daher, da will ich mir die Artikelliste auch nicht mit zu vielen Ausrufezeichen zukleistern. Und das ist der Code:</p>
<pre class="brush: php; title: ; notranslate">
/**
 * Adds a warning column to post list admin screens.
 *
 * @param array $posts_columns An array of columns.
 * @param string $post_type The name of the post type.
 * @return array The modified array of columns.
 */
function jf_add_warning_column($posts_columns, $post_type) {
  if ($post_type != 'page') {
    $posts_columns = array_merge(array_slice($posts_columns, 0, 1),
        array('thumbnail_warning' =&gt; ''),
        array_slice($posts_columns, 1));
  }
  return $posts_columns;
}

add_filter('manage_posts_columns', 'jf_add_warning_column', 10, 2);

/**
 * Displays the newly added warning column on post list admin screens. Doesn't
 * return anything, just outputs the column's content for the current post.
 *
 * @param string $column_name The column name.
 * @param int $post_id The ID of the current post.
 */
function jf_display_warning_column($column_name, $post_id) {
  if ('thumbnail_warning' == $column_name) {
    // check if the post has a post thumbnail
    if (has_post_thumbnail($post_id)) {
      return;
    }

    // check if the post has an image assigned
    $args = array(
      'numberposts' =&gt; 1,
      'post_mime_type' =&gt; 'image',
      'post_parent' =&gt; $post_id,
      'post_type' =&gt; 'attachment'
    );
    if (sizeof(get_children($args)) &gt; 0) {
      echo '&lt;img src=&quot;' . get_stylesheet_directory_uri() . '/images/exclamation.png&quot; '
        . ' width=&quot;16&quot; height=&quot;16&quot; alt=&quot;Warnung&quot; title=&quot;Warnung: Artikelbild ist nicht gesetzt!&quot; /&gt;';
    }
  }
}

add_action('manage_posts_custom_column', 'jf_display_warning_column', 10, 2);</pre>
<p class="tested_with">Getestet mit: WordPress 3.3</p>
<p>Der Code dafür kann in ein kleines <a href="http://code.freudendahl.net/2012/02/wordpress-klassenbasiertes-funktions-plugin/">Funktions-Plugin</a>, wo ihr Code-Snippets ablegen könnt, oder zur Not auch in die functions.php eures Themes. Das verwendete Icon stammt aus der <a href="http://www.famfamfam.com/lab/icons/">FamfamFam-Iconsammlung</a>, ihr könnt natürlich ein Icon eurer Wahl nehmen. Nicht vergessen, den Pfad entsprechend anzupassen (wenn der Code in einem Plugin landet, dann plugins_url(&#8216;images/exclamation.png&#8216;, __FILE__) statt get_stylesheet_directory_uri() für die Bild-URL verwenden).</p>
<p><strong>Update 10. Feb:</strong> Oh, the irony. Um diesen Beitrag mit einem Screenshot der Warnung illustrieren zu können habe ich nicht sofort ein Artikelbild zugewiesen. Zwei Wochen später schaue ich nun gerade zufällig auf die Artikelliste und sehe das Warn-Icon, was mich daran erinnert, hier mal noch ein Artikelbild zuzuweisen. <img src='http://code.freudendahl.net/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley colorbox-39' /> </p>
<p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></content:encoded>
			<wfw:commentRss>http://code.freudendahl.net/2012/01/wordpress-admin-anpassen-fehlende-artikelbilder-markieren/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Missverständnis: WordPress zeigt Auszüge im Feed an</title>
		<link>http://code.freudendahl.net/2012/01/missverstaendnis-wordpress-zeigt-auszuege-im-feed-an/</link>
		<comments>http://code.freudendahl.net/2012/01/missverstaendnis-wordpress-zeigt-auszuege-im-feed-an/#comments</comments>
		<pubDate>Fri, 20 Jan 2012 07:25:03 +0000</pubDate>
		<dc:creator>Johannes</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Feed]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://code.freudendahl.net/?p=36</guid>
		<description><![CDATA[In den WordPress-Einstellungen kann man auswählen, ob im Feed die kompletten Artikel oder nur Auszüge angezeigt werden sollen. Schaut man sich danach den eigenen Feed an, ist es leicht, einem Missverständnis aufzusitzen und sich dann sehr zu wundern. Mir ging es beim Einrichten dieser Seite so: Ich habe den Feed auf die Anzeige der kompletten Artikel konfiguriert, rief dann den Feed im Firefox auf und sah verwundert, dass dort nur &#8230; <a href="http://code.freudendahl.net/2012/01/missverstaendnis-wordpress-zeigt-auszuege-im-feed-an/">Weiterlesen <span class="meta-nav">&#8594;</span></a><p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></description>
			<content:encoded><![CDATA[<p><img src="http://code.freudendahl.net/files/2012/01/firefox_feed-300x228.jpg" alt="Firefox-Feedansicht" width="300" height="228" class="alignleft size-medium wp-image-37 colorbox-36" />In den WordPress-Einstellungen kann man auswählen, ob im Feed die kompletten Artikel oder nur Auszüge angezeigt werden sollen. Schaut man sich danach den eigenen Feed an, ist es leicht, einem Missverständnis aufzusitzen und sich dann sehr zu wundern. Mir ging es beim Einrichten dieser Seite so: Ich habe den Feed auf die Anzeige der kompletten Artikel konfiguriert, rief dann den Feed im Firefox auf und sah verwundert, dass dort nur die Kurzfassungen der Artikel angezeigt wurden. Lag es am Theme? Oder gar an WordPress selber? Ich habe eine ganze Weile herumgesucht, bis ich auf meinen Fehler kam: Der Feed ist völlig in Ordnung, Firefox zeigt einfach nur die Kurzfassungen an.</p>
<p>Dazu muss man sich bewusst machen, dass die graphische Ansicht, die man beim Aufruf eines Feeds im Firefox angezeigt bekommt, nicht den Feed selber zeigt sondern Firefox&#8216; Sicht darauf. In dieser Hinsicht verhält sich der Browser auch nur wie ein Feed-Reader: Er reichert die Feed-Darstellung optisch an und entscheidet,<em> welche Feed-Inhalte angezeigt werden</em>. Erst wenn man sich den Seitenquelltext anschaut, sieht man den Feed selber. Darin enthalten ist jeweils die Langfassung des Artikels (content:encoded-Element) sowie die Kurzfassung (description-Element). Ein Feed-Reader kann sich aussuchen (oder den Nutzer aussuchen lassen), welche der beiden Varianten dargestellt werden soll. Im Firefox kann man das meines Wissens nach nicht einstellen, dort sieht man immer die Kurzfassung, auch wenn der komplette Artikel vorhanden ist.</p>
<p>Die Einstellungen für den Feed sind im WordPress-Adminbereich übrigens unter Einstellungen &gt; Schreiben &gt; &#8222;Zeige im Newsfeed&#8220; zu finden. Man kann wählen zwischen &#8222;ganzer Text&#8220; und &#8222;Kurzfassung&#8220;, Standard-Wert ist &#8222;Kurzfassung&#8220;.</p>
<p>                        <p style="border-top: 1px solid #999999; padding-top: 1em;"><small>Soweit nicht anders angegeben: &copy; Johannes Freudendahl. Lesen erwünscht, Zitieren erlaubt, Übernahme des kompletten Textes oder Einbinden des Feeds in andere Webseiten nicht gestattet! Für mehr Infos dazu siehe <a href="http://code.freudendahl.net/ueber-die-seite/">http://code.freudendahl.net/ueber-die-seite/</a>.</small></p></p>
]]></content:encoded>
			<wfw:commentRss>http://code.freudendahl.net/2012/01/missverstaendnis-wordpress-zeigt-auszuege-im-feed-an/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

