Druck machen – xtcModified on steroids

Diesmal wollen wir im Rahmen der Artikelserie zur Geschwindigkeitsoptimierung mal ein bisschen Druck ausüben, denn die Steigerung von 36 auf 60 Punkten ist bestenfalls ein Teilerfolg.

Komprimierung aktivieren

Die Komprimierung der folgenden Ressourcen mit gzip könnte ihre Übertragungsgröße um 90.0 KiB verringern (Reduzierung um 70%).

70% der zu übertragenden, komprimmierungsfähigen Daten könnte man sich also sparen, wenn man diese vor der Auslieferung zippt. Das Entpacken findet dann beim Empfänger durch den Browser transparent statt. Jeder moderne Browser kann damit umgehen. Will man es auf die Goldwaage legen könnte man argumentieren, dass der Browser durch den erforderlichen Entpackvorgang länger braucht, um die Seite zu rendern. Aber das Nadelöhr ist nach wie vor ganz klar die Bandbreite der Leitung zum Empfänger, während die Prozessoren in moderner (Desktop-)Hardware gerade mal müde lächeln, wenn sie komprimierte Daten vorgesetzt bekommen.
Für die meisten eher unbedeutend ist in Zeiten von Traffic-Flatrates auch für Server die Einsparung beim Datenvolumen. Aber spätestens beim Thema CDN, wo nach wir vor nach Traffic abgerechnet wird, wird es plötzlich wieder interessant.

Komprimierung? Bringt der Shop doch schon mit!

php bietet von Haus aus die Möglichkeit, Daten komprimiert auszuliefern und in xtcModified ist die Option im Admin unter ‘Erweiterte Konfiguration’ – ‘Gzip Kompression’ auch schnell gefunden und aktiviert. Nur: Das gelbe Ausrufezeichen in Page Speed verschwindet nicht, die Note von YSlow für die Komprimierung wird nur marginal besser.

Der Grund ist einfach: Die shopeigene Komprimierung wird wie gesagt vom php-Parser zur Verfügung gestellt. Nur Dateien, die diesen vor Auslieferung passieren, werden komprimiert. Und genau das gilt nicht für CSS und Javascript. Aber gerade hier findet sich ein enormes Einsparpotential.

mod_deflate kümmert sich um alles

Auch hier kommt uns wieder ein Apache-Modul zu Hilfe: mod_defalte (seit Apache 2, davor hieß es mod_gzip und wer noch einen Apache 1 im Einsatz hat sollte seinen Serveradmin mal fragen, ob er nicht lieber mit etwas Geld verdienen möchte, womit er sich auch auskennt). Und auch hier lohnt ein Blick in die .htaccess des HTML5Boilerplate. Zuvor schalten wir aber die shopeigene Komprimierung wieder aus, sie wird nicht benötigt.

<IfModule mod_deflate.c>
 
# Force deflate for mangled headers developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping/
<IfModule mod_setenvif.c>
  <IfModule mod_headers.c>
    SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)s*,?s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding
    RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding
  </IfModule>
</IfModule>
 
# HTML, TXT, CSS, JavaScript, JSON, XML, HTC:
<IfModule filter_module>
  FilterDeclare   COMPRESS
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/html
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/css
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/plain
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $text/x-component
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/javascript
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/json
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/xhtml+xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/rss+xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/atom+xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/vnd.ms-fontobject
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $image/svg+xml
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $application/x-font-ttf
  FilterProvider  COMPRESS  DEFLATE resp=Content-Type $font/opentype
  FilterChain     COMPRESS
  FilterProtocol  COMPRESS  DEFLATE change=yes;byteranges=no
</IfModule>
 
<IfModule !mod_filter.c>
  # Legacy versions of Apache
  AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
  AddOutputFilterByType DEFLATE application/javascript
  AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
  AddOutputFilterByType DEFLATE application/xhtml+xml application/rss+xml application/atom+xml
  AddOutputFilterByType DEFLATE image/svg+xml application/vnd.ms-fontobject application/x-font-ttf font/opentype
</IfModule>
</IfModule>

Zunächst kümmert man sich um Clients, die aufgrund von Proxies oder anderer Einflüsse kaputte Header schicken und standardmäßig nicht mit komprimierten Inhalten versorgt werden würden. Gemäß einem Blogpost im Yahoo! Developer Network sind das ca. 15% der Besucher, also eine nicht zu unterschätzende Anzahl.

Anschließend werden mit Hilfe von mod_filter, das wesentlich flexibler ist als das früher benutzte AddOutputFilterByType, Inhalte in Abhängigkeit ihres MIME-Type (den wir ja schon im zweiten Teil gerade gezogen haben) komprimiert. Bei Bildern beispielsweise wäre das nur verschwendete Rechenleistung. Der letzte Block ist für Server, die ohne mod_filter laufen, indem die ‘alte’ mod_deflate-Syntax zum Einsatz kommt.

Wer jetzt neu lädt wird sich wundern, dass sich nach wie vor nichts tut, unsere Javascript- und CSS-Daten noch immer bemängelt werden. Nachdem wir aber bereits im vorherigen Teil die Expire-Zeiten hoch gesetzt haben fragt der Browser gar nicht mehr beim Server nach, sondern holt die Datei aus seinem Cache und die ist natürlich unkomprimiert.
Um realistische Ergebnisse aus Page Speed zu bekommen müssen wir also den Browsercache umgehen, entweder indem wir ihn leeren oder indem wir mit gedrückter Hochstelltaste auf das ‘Neu laden’-Icon klicken – zumindest in Firefox und Chrome führt das zum gewünschten Ergebnis. Das ist aber auch nur deshalb praktikabel, weil sich unser Shop noch im Testbetrieb befindet. Wie man das Problem im Livebetrieb umgeht haben wir ja schon behandelt.

YSlow jetzt mit der Gesamtnote A

YSlow jetzt mit der Gesamtnote A

Auch bei Page Speed die 75%-Marke überschritten

Auch bei Page Speed die 75%-Marke überschritten

Natürlich gibt es auch die neue .htaccess mit dem aktuellen Status zum Dowload.

Weitere Artikel der Serie

  1. Höher, schneller, weiter
  2. Verfallsdatum überschritten

Disclaimer: Ich bin nach wie vor der Meinung, dass viele der Einstellungen in der Serverkonfiguration besser aufgehoben sind als in der .htaccess-Datei, da aber sicherlich viele das ganze auch auf Shared Hosting umsetzen möchten werden alle Änderungen, die auch über die .htaccess durchgeführt werden können, im Rahmen dieser Artikelreihe dort vorgenommen.

Verfallsdatum überschritten – xtcModified on steroids

36 von 100 möglichen Punkten erreicht ein frisch installierter xtcModified auf einem 1&1 Virtual Server. Diesen Wert gilt es jetzt im Rahmen dieser Artikelserie zu optimieren.

Browser-Caching nutzen

Ganz oben auf der Liste von Page Speed steht das fehlende Browser Caching.

Die Aktualität der folgenden Cache-fähigen Ressourcen ist nur von kurzer Dauer. Legen Sie fest, dass folgende Ressourcen künftig mindestens einmal pro Woche ablaufen:

Und darunter kommt so ziemlich jedes Bild, das Stylesheet und die Javascript-Dateien, die alle kein Ablaufdatum haben. Dieses würde festlegen, wie lange der Browser oder auch ein Proxyserver die Datei als aktuell betrachtet. Mit der aktuellen Konfiguration fordert er jede Datei bei jedem Aufruf erneut an, denn kein Ablaufdatum bedeutet, dass die Datei aus Browsersicht sofort veraltet ist.

Ein solches Verhalten ist sinnvoll und wünschenswert bei Inhalten, die bei jeder Anforderung die aktuellen Gegebenheiten reflektieren müssen, so führt beispielsweise das hinzufügen eines Artikels zum Warenkorb zu einer Änderung und das HTML für den Warenkorb darf dann eben nicht aus dem Cache geholt werden. Bilder, Stylesheets und Scripts ändern sich aber in der Regel nicht mit jedem Klick. Es ist also völlig unnötig, diese Dateien jedes Mal erneut anzufordern bzw. auch nur beim Server anzufragen, ob sich was geändert hat.

mod_expires for the rescue

Zwar gibt es in der .htaccess-Datei, die mit xtcModified mitkommt, einen Eintrag, um bestimmte Formate mit einem Ablaufdatum von einem Monat zu versehen (der aber standardmäßig nicht aktiv ist), ich bevorzuge hier aber die feiner aufgranulierte Lösung aus dem HTML5Boilerplate:

<IfModule mod_expires.c>
  ExpiresActive on
 
# Perhaps better to whitelist expires rules? Perhaps.
  ExpiresDefault                          "access plus 1 month"
 
# cache.appcache needs re-requests in FF 3.6 (thanks Remy ~Introducing HTML5)
  ExpiresByType text/cache-manifest       "access plus 0 seconds"
 
# Your document html 
  ExpiresByType text/html                 "access plus 0 seconds"
 
# Data
  ExpiresByType text/xml                  "access plus 0 seconds"
  ExpiresByType application/xml           "access plus 0 seconds"
  ExpiresByType application/json          "access plus 0 seconds"
 
# Feed
  ExpiresByType application/rss+xml       "access plus 1 hour"
  ExpiresByType application/atom+xml      "access plus 1 hour"
 
# Favicon (cannot be renamed)
  ExpiresByType image/x-icon              "access plus 1 week" 
 
# Media: images, video, audio
  ExpiresByType image/gif                 "access plus 1 month"
  ExpiresByType image/png                 "access plus 1 month"
  ExpiresByType image/jpg                 "access plus 1 month"
  ExpiresByType image/jpeg                "access plus 1 month"
  ExpiresByType video/ogg                 "access plus 1 month"
  ExpiresByType audio/ogg                 "access plus 1 month"
  ExpiresByType video/mp4                 "access plus 1 month"
  ExpiresByType video/webm                "access plus 1 month"
 
# HTC files  (css3pie)
  ExpiresByType text/x-component          "access plus 1 month"
 
# Webfonts
  ExpiresByType font/truetype             "access plus 1 month"
  ExpiresByType font/opentype             "access plus 1 month"
  ExpiresByType application/x-font-woff   "access plus 1 month"
  ExpiresByType image/svg+xml             "access plus 1 month"
  ExpiresByType application/vnd.ms-fontobject "access plus 1 month"
 
# CSS and JavaScript
  ExpiresByType text/css                  "access plus 1 year"
  ExpiresByType application/javascript    "access plus 1 year"
 
  <IfModule mod_headers.c>
    Header append Cache-Control "public"
  </IfModule>
 
</IfModule>

Das ganze in If-Bedingungen zu verpacken verhindert effektiv Internal Server Errors (500), wenn das Modul nicht installiert ist. Klar, dass das ganze dann auch nichts bringt, aber ein Shop, der Inhalte ohne die Optimierung ausliefert ist immer noch besser als ein Shop, der nur eine Fehlermeldung zurückgibt.
Wenn aber mod_expires aktiv ist geschieht folgendes: Der generelle Wert für das Ablaufdatum wird auf einen Monat gesetzt und das ganze dann für Inhalte, die nicht gecached werden sollen, wieder zurückgesetzt. Anschließend werden die Werte für einzelnen Formate basierend auf dem MIME-Type gesetzt. Man könnte alles weglassen, was dem generellen Wert entspricht, aber ich lasse das hier mal mit aufgeführt, vielleicht will man den ein oder anderen Ablaufzeitraum doch anpassen, bei X-Skating sind es beispielsweise 10 Jahre für Bilder. Auf die dabei auftretenden Probleme gehe ich später noch ein.
Cache-Control “public” sorgt dann noch dafür, dass diese Inhalte auch von Proxies gecached werden können, auch wenn man sich vorher per HTTP authentifizieren musste (was für xtcModified aber nicht zutrifft).

Da mod_expires auf Grundlage der MIME-Types arbeitet empfiehlt es sich, noch einen anderen Bereich aus der Datei von HTML5Boilerplate zu übernehmen, nämlich den Teil, wo die MIME-Types vereinheitlicht werden, gerade Javascript kommt je nach Serverkonfiguration mit den obskursten MIME-Types daher:

# JavaScript
#   Normalize to standard type (it's sniffed in IE anyways)
#   tools.ietf.org/html/rfc4329#section-7.2
AddType application/javascript         js
 
# Audio
AddType audio/ogg                      oga ogg
AddType audio/mp4                      m4a
 
# Video
AddType video/ogg                      ogv
AddType video/mp4                      mp4 m4v
AddType video/webm                     webm
 
# SVG
#   Required for svg webfonts on iPad
#   twitter.com/FontSquirrel/status/14855840545
AddType     image/svg+xml              svg svgz
AddEncoding gzip                       svgz
 
# Webfonts
AddType application/vnd.ms-fontobject  eot
AddType application/x-font-ttf         ttf ttc
AddType font/opentype                  otf
AddType application/x-font-woff        woff
 
# Assorted types
AddType image/x-icon                        ico
AddType image/webp                          webp
AddType text/cache-manifest                 appcache manifest
AddType text/x-component                    htc
AddType application/x-chrome-extension      crx
AddType application/x-opera-extension       oex
AddType application/x-xpinstall             xpi
AddType application/octet-stream            safariextz
AddType application/x-web-app-manifest+json webapp
AddType text/x-vcard                        vcf

Die einzige Änderung, die wir noch an den Expires vornehmen ist das Favicon, das wird ebenfalls auf einen Monat gesetzt. Die .htaccess, wie sie bis zu diesem Zeitpunkt aussieht, kann hier heruntergeladen werden.

Erneut durch YSlow und Page Speed überprüft ergibt sich schon ein wesentlich besseres Bild. YSlow pendelt sich jetzt bei 89 Punkten ein, noch deutlicher ist die Steigerung bei Page Speed, 60 von 100 möglichen Punkten stehen jetzt bereits zu Buche.

YSlow klettert von 80 auf 89 Punkte...


...Page Speed sogar von 36 auf 60

Auch schön zu sehen ist die extreme Reduktion an HTTP-Requests durch diese Maßnahme. So zeigt das Wasserfalldiagramm bei webpagetest.org ohne mod_expire auch bei der Anfragewiederholung noch alle Bilder etc. Der Server antwortet zwar mit 304 Not Modified, aber die Anfrage geht trotzdem raus und muss beantwortet werden. Sind Expire-Header gesetzt gehört das der Vergangenheit an.

Inhalte aktualisieren bei langen Expires

(Sehr) lange Expire-Zeiten bringen ein Problem mit sich: Da der Browser davon ausgeht, dass die CSS-Datei, die er gestern geladen hat, noch ein Jahr gültig ist, wird er erst gar nicht auf die Idee kommen, diese neu anzufordern. Daher kommt man um eine Versionierung nicht herum. HTML5Boilerplate empfiehlt das über Query-Paramter zu machen, statt stylesheet.css ruft man also beispielsweise stylesheet.css?20120112 auf. Damit man das nicht jedes Mal händisch aktualisieren muss sollte man die Zeilen 15 und 16 in templates/xtc5/css/general.css.php wie folgt ändern:

<link rel="stylesheet" href="<?php echo 'templates/'.CURRENT_TEMPLATE; ?>/stylesheet.css?<?php echo filemtime('templates/'.CURRENT_TEMPLATE.'/stylesheet.css') ?>" type="text/css" />
<link rel="stylesheet" href="<?php echo 'templates/'.CURRENT_TEMPLATE; ?>/css/thickbox.css?<?php echo filemtime('templates/'.CURRENT_TEMPLATE.'/css/thickbox.css') ?>" type="text/css" media="screen" />

Damit wird immer das Datum der letzten Modifikation als Unix-Zeitstempel angehangen. Das gleiche sollte man auch für das Javascript machen, aber da wir uns dem eh im Rahmen dieser Serie noch gesondert annehmen müssen verschieben wir das auf einen späteren Zeitpunkt.

Als letztes Problemkind bleiben die Bilder. Die Lösung dafür ist etwas aufwendiger und wird behandelt werden wenn wir auf das Thema Content Delivery Networks (CDN) zu sprechen kommen.

Weitere Artikel der Serie


Disclaimer: Ich bin nach wie vor der Meinung, dass viele der Einstellungen in der Serverkonfiguration besser aufgehoben sind als in der .htaccess-Datei, da aber sicherlich viele das ganze auch auf Shared Hosting umsetzen möchten werden alle Änderungen, die auch über die .htaccess durchgeführt werden können, im Rahmen dieser Artikelreihe dort vorgenommen.

Höher, schneller, weiter – xtcModified on steroids

So langsam erreicht die Erkenntnis, dass Websitegeschwindigkeit (auch) ein Rankingkriterium sein kann, auch die kleineren und ganz kleinen Shops.

Zeit, in einer Artikelserie mal zu zeigen, wie man aus einem der derzeit beliebtesten Open-Source-Systeme, nämlich aus xtcModified, das Maximum rauskitzeln kann.

Ausgangssituation

Shopsystem

Es beginnt mit einem xtcModified, wie er aus der Schachtel fällt. Zum Zeitpunkt dieser Artikelserie trägt er den schönen und leicht zu merkenden Namen 1.05 SP1b. In diesen wurden die Demoartikel importiert, allerdings mit individuellen Artikelnummern, damit es mehr als vier unterschiedliche Produkte sind. Außerdem wurden noch SEO-URLs aktiviert und vier Artikel auf die Startseite gestellt.

Hosting

Installiert ist der Shop auf einem 1&1 Virtual Server L, denn nur so hat man die maximale Kontrolle. Das sollte man im Hinterkopf behalten, wenn man die im Rahmen dieser Serie vorgestellten Tipps auf einem Shared Hosting umsetzen will, manche Sachen gehen dort einfach nicht.

Domain

Ach ja, Domains kauft man nicht, Domains hat man. Es stimmt, dass man die bei eigentlich jedem Hosting hinterherworfen bekommt. Trotzdem sollte man darauf achten, dass man eine größtmögliche Flexibilität bei der Konfiguration hat.
10 Subdomains, wie es 1&1 beispielsweise früher für einen Server angeboten hat, sind eine absolut sinnlose künstliche Limitation. Domaindiscount24 hat sich in den vergangenen Jahren als zuverlässiger Partner erwiesen was das Domainhosting angeht. Wer keinen eigenen Server hat sollte aber vorher mit seinem Hoster klären, ob dieser Fremddomains einrichten kann. Bei den meisten geht das kostenlos, bei manchen muss man eine geringe monatliche Gebühr einplanen.

Tools

Yahoos YSlow und Googles Page Speed sind sicherlich die beiden bekanntesten Browser-Plugins, die sich dem Thema Websitegeschwindigkeit widmen. Diese beiden werden uns im Rahmen der Serie begleiten, hin und wieder aber vom ein oder anderen Online-Tool ergänzt werden. Die Kritierien, die beide überprüfen, ähneln sich, die Ergebnisse könnten aber unterschiedlicher nicht sein.

Ausgangssituation YSlow

Während YSlow 80 Punkte vergibt und damit auf die amerikanische Schulnote ‘B’ kommt kann sich Page Speed bei unserem Testshop unter www.ladeze.it lediglich für 36 von 100 Punkten erwärmen, was zu einem roten Gesamtwert führt.

Ausgangssituation PageSpeed

Damit konfrontiert geht es jetzt an die Optimierung, denn auch beim ‘B’ von YSlow ist deutlich Optimierungspotential zu sehen, gibt es doch für den ein oder anderen Punkt eine glatte Sechs.

Wer sich die Wartezeit bis zum zweiten Teil etwas verkürzen will sollte in den Podacst von OnlineRader reinhören. Der bietet einen guten Überblick zum Thema.


Disclaimer: Ich bin nach wie vor der Meinung, dass viele der Einstellungen in der Serverkonfiguration besser aufgehoben sind als in der .htaccess-Datei, da aber sicherlich viele das ganze auch auf Shared Hosting umsetzen möchten werden alle Änderungen, die auch über die .htaccess durchgeführt werden können, im Rahmen dieser Artikelreihe dort vorgenommen.

Vertrauensbildende Maßnahmen

Vertrauen schaffen zwischen dem Händler und dem Kunden ist eine der Grundvoraussetzungen für erfolgreiche Geschäfte im Online-Handel. Ein mit SSL verschlüsselter Checkoutprozess war für uns daher schon immer eine Selbstverständlichkeit – nicht jeder Mitbewerber sieht das so. Darüber hinaus arbeiten wir seit Jahren erfolgreich mit Trusted Shops zusammen. Im vergangenen Jahr haben wir unser Engagement in diesem Bereich noch weiter ausgebaut und lassen unsere Webseiten täglich von McAfee Secure auf mögliche Schwachstellen prüfen – als erster und selbst zum Zeitpunkt dieses Posts ein Jahr später einziger Anbieter im Bereich Erzgebirge Volkskunst (Erzgebirge Palast) und Kuckucksuhren (Schwarzwald Palast).

In diesem Jahr gehen wir noch einen Schritt weiter. Unser Anbieter für SSL-Zertifikate, die PSW Group, hat uns in Zusammenarbeit mit Comodo ein Paket geschnürt, mit dem wir die Überprüfung auf Schwachstellen nicht nur auf alle unsere Shops ausweiten, sondern darüber hinaus auch noch alle Shops mit sogenannten Extended-Validation-Zertifikaten versehen können, die neben der schon vorhandenen Verschlüsselung auch klar und deutlich zum Ausdruck bringen, wer hinter der Website steckt und damit noch mehr Sicherheit für unsere Kunden bieten.
Damit übernehmen wir beim Thema sicheres Einkaufen nicht nur im Erzgebirge und im Schwarzwald die Vorreiterrolle, sondern jetzt auch im Bereich Nordic Cross Skating und lassen damit teilweise weit größere und umsatzstärkere Konkurrenzunternehmen hinter uns.

Konfigurierbare Produkte mit individuellen Preisen

Bei manchen Funktionalitäten, die in Magento eingebaut sind, fragt man sicht schon, ob man bei Varien mit dem, was man sich da zusammenklöppelt, auch einen Praxisbezug sieht oder ob man einfach irgendwas programmiert, egal ob es in der Praxis brauchbar ist oder nicht. Die Preisvergabe bei konfigurierbaren Produkten ist so ein Fall. Magento sieht von Haus aus vor, dass man einer Variante, etwa einer anderen Größe, einen festen oder prozentualen Aufpreis geben kann. Oder anders gesagt, die untenstehende Preismatrix funktioniert mit diesem System schon nicht mehr:

  11cm 15cm
natur 28,40 31,80
Goldrand 31,20 34,90
lasiert 34,60 40,70

Denn bei natur kostet die 15cm-Variante 3,40 Euro mehr, bei Goldrand aber schon 3,70 Euro und bei lasiert gar 6,10 Euro. Das lässt sich weder durch einen Fixpreis noch durch einen prozentualen Aufschlag abdecken und wenn man dann noch bedenkt, dass man das auch noch für die Ausführung (natur, Goldrand, lasiert) machen muss bricht das ganze System in sich zusammen.

Zum Glück gibt es eine Lösung, die eigentlich so naheliegend ist, dass man sich fragt, warum Magento das nicht einfach von Haus aus kann: Simple Configurable Products. Die Erweiterung sorgt dafür, dass einfach der direkt beim Produkt hinterlegte Preis benutzt wird. Und nicht irgendwelche komischen Berechnungen durchgeführt werden, die am Ende doch nicht zum gewünschten Ergebnis führen.

Einen kleinen Bug hat die Erweiterung aber: Zeigt man die gewählte Option im Warenkorb an, dann wird immer das Admin-Label für das Attribut verwendet, welches nicht mehrsprachig ist. Aber dafür gibt es Abhilfe. Die Datei, um die es geht, ist Checkout/Block/Cart/Item/Renderer.php. Dort findet sich in Zeile 81:

'label' => $attribute->getFrontendLabel(),

Das muss lediglich durch

'label' => $attribute->getStoreLabel(),

ersetzt werden und schon sind die Label auch in der jeweiligen Store-Sprache.

Umlaute im URL key von Magento

Umlaute ist ja eines meiner Lieblingsthemen. Und Magento stellt sich da direkt hinter Afterbuy in die ‘Können wir nicht’-Schlange. Naja, fast. So heißt es in meinem Lieblingsbuch auf Seite 162:

Die URL-Bezeichnung ist wichtig, wenn Sie als Shop-Besitzer die Anzeige in der Adressleiste eines Browsers beeinflussen möchten. Sollten Sie das Feld leer lassen, generiert Magento automatisch aus dem Produktnamen die URL-Bezeichnung. Da das System aus dem USA stammt, werden die Umlaute dabei nicht berücksichtigt, sondern falsch dargestellt: ä als a, ö als o etc.

Soweit richtig. Magento berücksichtigt in der URL-Bezeichnung (URL key) keine Umlaute. Warum man im Buch aber nicht direkt auf die Lösung verweist erschließt sich mir nicht. Denn wer mag schon bei vierstelligen Produktzahlen jeden URL key in Handarbeit erstellen?

Allerdings muss die im Magento-Wiki befindliche Lösung noch etwas erweitert werden, denn die Einträge in app/etc/local.xml, die im Wiki genannt werden, berücksichtigen weder ß noch die Großbuchstaben Ä, Ö und Ü (die Ersetzung erfolgt offensichtlich bevor der String in Kleinbuchstaben gewandelt wird).

Der fertige Block, der direkt vor </config> eingesetzt wird, muss also lauten:

<default>
  <url>
    <convert>
      <char0228><from>ä</from><to>ae</to></char0228>
      <char0246><from>ö</from><to>oe</to></char0246>
      <char0252><from>ü</from><to>ue</to></char0252>
      <char0223><from>ß</from><to>ss</to></char0223>
      <char0196><from>Ä</from><to>ae</to></char0196>
      <char0214><from>Ö</from><to>oe</to></char0214>
      <char0220><from>Ü</from><to>ue</to></char0220>
    </convert>
  </url>
</default>

Damit werden Umlaute im Produktnamen im URL key korrekt umgeschrieben. Die Einstellung greift nicht für Kategorien. Wer dafür eine Lösung hat, immer her damit, aber Kategorien legt man ja auch nicht so oft neu an wie Produkte bzw. nicht in der selben Anzahl.

xt:Commerce: Bestellnummer mit Datum

Das ist jetzt zur Abwechslung mal keine 1:1-Anleitung, die man einfach mittels Copy&Paste übernehmen könnte, sondern eher ein Beitrag, der einem eine Idee geben soll, wie man das Problem angehen kann. Er setzt voraus, dass man sich einigermaßen gut mit php und dem Shopsystem auskennt und in der Lage ist, Transferleistungen vorzunehmen. Wer sich das nicht zutraut sollte die Finger davon lassen oder nachher nicht rumheulen.

Die Frage kommt immer mal wieder und sie ist meiner Meinung nach auch durchaus berechtigt: Wie kann man die Bestellnummer eines xt:Commerce-Systems mit zusätzlichen Informationen anreichern, sei es ein Shopkürzel, das Datum oder auch einfach einer Zufallszahl? Die kurze Antwort ist: Geht nicht, denn xt:Commerce nimmt die inkrementell von MySQL erzeugte ID als Bestellnummer. Die lange Antwort ist: Geht schon, erfordert aber tiefe Eingriffe ins System. Updatefähigkeit ade. Aber das ist ja mit jedem xt:Commerce-‘Modul’ so.

Beginnen wir mit dem leichten Teil der Übung. Einer Funktion, die eine Bestellnummer mit Datum zurück liefert. Dazu brauchen wir neben der systemseitig generierten ID noch das Bestelldatum. Sonst haben wir morgen eine andere Bestellnummer als heute. Die Datei packen wir in das inc/-Verzeichnis und nennen sie xtc_build_order_id.php.

function xtc_build_order_id($date, $id) {
	$date = strtotime($date);
	return sprintf("%d%02d%02d-%05d", strftime("%y", $date), strftime("%m", $date), strftime("%d", $date), $id);
}

Der anstrengende Teil kommt jetzt: Überall, wo die Bestellnummer zur Ausgabe verwendet wird, muss der Funktionsaufruf ergänzt werden. Wir reden mindestens von diesen Dateien:

account_history_info.php
account_history.php
account.php
checkout_success.php
print_order.php
send_order.php

Dazu kommen alle Zahlungsmodule, sofern diese die Bestellnummer ausgeben oder benutzen (z.B. Paypal) und mindestens die orders.php im admin/-Verzeichnis. In jeder dieser Dateien muss am Anfang, nach den schon vorhanden includes, zunächst unsere Funktionsdatei inkludiert werden:

require_once(DIR_FS_INC.'xtc_build_order_id.inc.php');

und wie gesagt die Bestellnummer durch den Funktionsaufruf ersetzt werden, so wird z.B. in der send_order.php aus

$smarty->assign('oID', $insert_id);

das hier:

$smarty->assign('oID', xtc_build_order_id($order->info['date_purchased'], $insert_id));

Was daran anstrengend ist? Man muss sich in jeder Datei wieder raussuchen, in welcher Variable die Order-ID versteckt ist. In send_order.php ist es wie gesehen $insert_id, in print_order.php hingegen $_GET['oID']. Denn im $order-Objekt kommt die ID selbst nicht vor – wäre ja auch zu einfach. Und mitunter, wie in account_history.php, hat man nicht einmal ein $order-Objekt zur Verfügung.
Man muss also gewillt sein, ein bisschen zu suchen. Und man muss wissen, was man tut.

Der +1-Button – die SEO-Revolution

Mit dem internationalen Rollout des +1-Buttons auf Webseiten tauchen auch hier die ersten Anleitungen auf, wie man das ganze beispielsweise in einen xt:Commerce-Shop integrieren kann.

+1 wird die Suche, wie wir sie kennen, revolutionieren. Und mit der Integration in die Google Webmaster Tools geht der Suchgigant gleich noch einen Schritt weiter und bietet eine Auswertung des Buttons in einem seiner Produkte, das jeder Shopbetreiber eh nutzen sollte.

Das ganze lässt sich mit wenigen Handgriffen in den eigenen Shop integrieren. Ich präferiere aber einen Ansatz, der nicht an allen möglichen Stellen im Template {php}-Tags nutzt, daher: In includes/application_bottom.php kurz vor dem schließenden <body>-Tag einfügen:

echo '<script type="text/javascript" src="https://apis.google.com/js/plusone.js">{lang:''.$_SESSION['language_code'].''}</script>';

An der Stelle in der Produktseite, wo der +1-Button erscheinen soll, muss dann nur noch entweder der Button in Googles XML-Syntax oder die HTML5-Variante eingefügt werden (HTML5 empfiehlt sich, wenn man das ganze fehlerfrei durch den Validator bekommen will):

<div class="g-plusone" data-size="medium"></div>

Das ganze kann dann so aussehen:

Produktseite mit +1-Integration

Die Angabe des Zählers kann man sich sparen, wenn man diesen sowieso anzeigen will. Die Angabe der URL kann man sich ebenfalls sparen, wenn man saubere URLs hat oder einen canonical-Tag benutzt. Die Finger lassen sollte man generell von den xt:Commerce eigenen ‘SEO-URLs’ – und damit ist auch die preg_replace()-Geschichte, wie bei Christian vorgeschlagen, vom Tisch. Saubere URLs, z.B. für den canonical-Tag, bekommt man für ein Produkt mittels

xtc_href_link(FILENAME_PRODUCT_INFO, xtc_product_link($product->data["products_id"]))

Wer mit irgendwelchen Umleitungen beispielsweise seiner Produktseiten arbeitet, um auch serverseitig für wirklich kanonische URLs zu sorgen, muss evtl. ein gesondertes Augenmerk auf die Funktion legen, die hier für die Redirects verantwortklich ist. Google überprüft nämlich die URL, indem sie aufgerufen wird. Landet diese Überprüfung in einem Redirect hat man schnell statt des +1-Buttons ein rotes Ausrufezeichen und im Firebug findet sich in der Antwort des entsprechendes Requests:

400 Bad Request: Cannot confirm a connection that was not proposed.

Im Log findet sich zum zugehörigen Request ein Client, der sich als ‘Firefox/1.0.4’ identifiziert. Da dieser in der freien Wildbahn faktisch ausgestorben ist kann man darauf eine mögliche Überprüfung aufbauen.

Magento-Buch – nur für wen?

Ich lese gerade, äh nein, ich quäle mich durch ein Magento-Buch, namentlich Magento – Installation, Anwendung, Erweiterung und ich habe mich auf Podcast-Empfehlungen und die Amazon-Bewertungen verlassen. Genau. Selber schuld.

Nach einem Drittel ist mir absolut unklar, für welche Zielgruppe das Buch ist. Auf jeden Fall trifft das, was Falk Opitz in seiner Rezension schreibt, nicht zu:

Dieses Buch ist sowohl für Neueinsteiger, als auch erfahrene Entwickler sehr zu empfehlen.

Leider bekomme ich jedes Mal Ausschlag, wenn Sachen, die für einen erfahrenen Entwickler eine Selbstverständlichkeit sind, erklärt werden. Ich brauche keine Empfehlungen für Texteditoren oder FTP-Programme und ich brauche keine Erklärungen, was ein Cache ist oder ein RSS-Feed. Auf der anderen Seite dürfte den Neueinsteiger schon die lokale Installation überfordern, spätestens beim Editieren von irgendwelchen E-Mail-Templates dürfte aber jemand, der HTML für einen US-Amerikanischen Radiosender hält, das Buch aus der Hand fallen.

Und dann kommen die wirklich wichtigen Sachen schlicht zu kurz.
Stichwort Zahlungsmodule. Steht auch ganz markant auf dem Buchtitel. Dafür steht dann auf Seite 116:

4.4.5 Google API, Paypal-Konten, System & Erweitert – Einstellungen für Entwickler
Innerhalb des Magento-Administrationsbereichs gibt es noch weitaus mehr Möglichkeiten und Konfigurationseinstellungen, als in diesem Kapitel behandelt wurdenwerden. Die für dieses Buch nicht relevanten Bereichen haben wir daher an dieser Stelle kurz der Vollständigkeit halber erwähnt. Es handelt sich dabei um Bereiche, die vor allem für Entwickler bzw. Administratoren mit einem hohen Wissensstand interessant sein dürften.

Und auf Seite 130 noch einmal ein Schlag ins Gesicht:

Ein sehr populärer Vertreter einer im Internet etablierten Zahlungsmöglichkeit ist Paypal. Paypal wird, neben Payflow und Authorize.net, von Magento direkt unterstützt. Es würde aber den Umfang dieses Buches springen, gezielt auf die jeweiligen Payment-Provider einzugehen.

Oder, in meinen Worten:
Wenn Paypal Ihre einzige Zahlungsmöglichkeit ist, kaufen Sie sich doch bitte ein anderes Buch.

Stichwort Steuersätze. Also mir ist ja unklar was es bei Varien zu rauchen gab als das implementiert wurde. Aber nach dem 3,5 Seiten im Buch, die sich mit diesem Thema beschäftigen, hat man am Ende mehr Fragen als vorher. So wird das Thema eines weltweiten Versands (und der damit verbundenen Herausforderungen an die Steuerberechnung) mit keinem Wort erwähnt und es wird auch nicht erklärt, wie man sicherstellt, dass ein Schweizer keine Steuer berechnet bekommt, ein Franzose hingegen schon. Man klebt einfach ein paar Sachen zusammen, die zum Großteil aus rein textlicher Information bestehen und dann wird’s schon passen. Da werde ich mich wohl doch an diese Anleitung halten müssen.

Ich befürchte ja, das mit mir und dem Buch wird nichts mehr. Mal sehen, wie viele Seiten es noch überlebt, bevor es in eine Ecke fliegt. Dem Kollegen, der für die Shoppflege zuständig ist, brauche ich es aber auch nicht in die Hand zu drücken. Dem ist das zu technisch. Der mehr oder weniger generalistische Ansatz des Buches dürfte damit gleichzeitig sein größtes Problem sein…

Update

Bin dann doch schon auf Seite 305. Da werden gerade CSS-Änderungen am Frontend vorgenommen:

background:#fff; border:1px solid #999; border-top:none;

Und direkt danach kommt:

Was hat das zu bedeuten? background: #fff ist der weiße Hintergrund; border:1px solid #999 sagt dem Browser, dass er um den ganzen Inhaltsbereich einen grauen Rahmen ziehen soll.

Mach Sachen! Wie gesagt, eine Zielgruppe ist für dieses Buch nicht auszumachen.

Nordic-Cross-Skating-Shop im neuen Gewand

Gestartet mit genau einem Produkt (in drei Farben) hat sich unser X-Skating-Shop über die Jahre zu einem der größten und kompetentesten Shops im Bereich Nordic Skating gemausert. Nicht nur Ersatzteile und Zubehör wie Lampen, Luftpumpen usw. haben wir seither ins Sortiment genommen, im letzten Jahr kam dann mit Powerslide auch ein weiterer Anbieter von Cross Skates hinzu – weitere günstige Set-Angebote, neue Ersatzteile und Zubehör. Mit der Ankündigung weiterer Modelle von Powerslide (XC Trail, XC Path) und Skike (Skike vX SOLO, Skike vX TWIN) war klar: Mit dem bestehenden Layout lässt sich unser Produktspektrum für den Kunden nicht mehr übersichtlich darstellen.

X-Skating Website vor dem Relaunch

Das Ergebnis ist seit Anfang März online.

X-Skating Website nach dem Relaunch

Shopsystem

Als Shop-Engine haben wir auf unsere bewährte, stark modifizierte Version von xt:Commerce gesetzt, quasi ein xt:Commerce on steroids.
Auch wenn die Originalversion mittlerweile sehr viele Jahre auf dem Buckel hat ist sie so sehr auf unsere Prozesse angepasst, dass sich ein Wechsel auf welches Shopsystem auch immer nicht gelohnt hätte. Allerdings wurde sie flottgemacht für die Zukunft und setzt jetzt konsequent auf UTF-8 als Zeichenkodierung. Zur Performanceoptimierung kann sie außerdem Bilddaten vom CDN laden, in unserem Fall Amazon CloudFront.

Layout

Im krassen Gegensatz zur antiquierten Shop-Engine steht hingegen HTML und CSS. HTML5 und CSS3 präsentieren das neue Layout dem Besucher. Die neuen Tags aus dem HTML5-Sprachschatz ergänzen und verbessern das bereits vorhandene semantische Markup, mit runden Ecken und Schlagschatten wird die Optik weiter aufgepeppt.

Inhalte auf den Produktseiten sind jetzt in Tabs sauber strukturiert, ein neuer Stocklängenrechner ersetzt die alte Übersichtstabelle und die neue Navigation mit Dropdownmenüs bietet uns quasi unendliche Erweiterungsmöglichkeiten. jQuery und der beeindruckende Nivo Slider kümmern sich darum, dass alles schick aussieht und auch in Browser-Fossilen wie beispielsweise dem Internet Explorer 6 zumindest benutzbar ist.

Für ein einheitliches Look&Feel wurde im Rahmen des Relaunch auch ein einheitliches Iconset für Zahlungsmöglichkeiten und Hersteller von uns entwickelt, da die verfügbaren Iconsets gerade für den europäischen Raum alle mehr oder weniger unbrauchbar sind.

Besondere Aufmerksamkeit haben wir auf den kompletten Checkoutprozess gelegt.
Direkt im Warenkorb kann man jetzt sein Lieferland auswählen und erhält sofort die Information, in welcher Höhe Versandkosten anfallen. Es gibt keine gesonderten Seiten mehr für Gast- und Kontobestellung, der Kunde wählt vielmehr im ersten Schritt, ob er ein Passwort vergeben möchte oder nicht. Ebenso kann er direkt bei der Angabe seiner Rechnungsadresse gleich eine abweichende Lieferadresse angeben und muss dies nicht, wie bei xt:Commerce üblich, in einem gesonderten Schritt tun. Auch gibt es zu jeder Zahlungsweise noch einmal detaillierte Hinweise, Zahlungsweise und Versandart sind auf einer Seite zusammengefasst. Auch ändert sich das Layout sobald man den Bestellprozess beginnt, sämtliche störenden Elemente werden dann ausgeblendet bzw. durch in diesem Stadium des Bestellvorgangs nützliche Informationen ersetzt. Wie sich das auf die Konversionrate auswirkt wird die Zukunft zeigen müssen.

Performance

Trotz allem technischen Schnick-Schnack sollte die Performance nicht zu kurz kommen. Neben dem Auslagern der Bilddaten auf ein CDN und der Komprimierung von CSS- und JS-Daten wurden auch kleinere Geschwindigkeitsoptimierungen vorgenommen, seien es Größenangaben bei den Bildern oder die Kombination von Hintergrundbildern zu CSS-Sprites.
Ein php-opcode-Cacher, im konkreten Fall XCache sorgt für schnelle Parsezeiten und Googles mod_pagespeed-Erweiterung für den Apachen optimiert noch dort etwas nach, wo es manuell zu aufwendig ist. In belastbaren Zahlen liest sich das dann z.B. so, dass die Startseite, gemessen mit den Pingdom Tools in 2,8 statt vorher 9,6 Sekunden lädt – trotz fast identischer Datenmenge!