Magento 1.x SOAP API: Filter-Beispiele für Bestellungen

Weil man im Web zu wenige Beispiele für Roh-Abfragen für die SOAP API v2 findet, hier ein paar Beispiele. Diese Abfragen können mit dem kostenfreien GUI-Tool Insomnia ausprobiert werden:

Auf der Kommandozeile lässt sich der Aufruf wie folgt umsetzen:

curl -id "requestBody.xml" https://xonu.de/index.php/api/v2_soap

Abfrage der Schnittstellenbeschreibung im WSDL-Format:

https://xonu.de/api/v2_soap/?wsdl=1

Alle GET-Anfragen werden an die folgende URL gesendet:

https://xonu.de/api/v2_soap

Request Body für Login:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Body>
        <MAGE:login xmlns:MAGE="https://gaiwan.de/api/v2_soap/?wsdl=1">
        	<MAGE:username>API-USER</MAGE:username>
        	<MAGE:apiKey>API-KEY</MAGE:apiKey>
        </MAGE:login>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Response Body mit sessionId für weitere Abfragen:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
	xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
	xmlns:ns1="urn:Magento"
	xmlns:xsd="http://www.w3.org/2001/XMLSchema"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
	<SOAP-ENV:Body>
		<ns1:loginResponse>
			<loginReturn xsi:type="xsd:string">SESSION-ID</loginReturn>
		</ns1:loginResponse>
	</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Einfacher Filter für zurückgestellte Bestellungen (status = holded):

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:Magento" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
   <soapenv:Header/>
   <soapenv:Body>
      <urn:salesOrderList soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <sessionId>SESSION-ID</sessionId>
            <filters>
                <filter>
                    <associativeEntity>
                        <key>status</key>
                        <value>holded</value>
                    </associativeEntity>
                </filter>
            </filters>
		 </urn:salesOrderList>
   </soapenv:Body>
</soapenv:Envelope>

Komplexer Filter für Bestellungen, die in August 2022 aktualisiert wurden und status = complete (Operatoren: eq – equal, lt – lower then, qt – greater then) haben:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:Magento" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
   <soapenv:Header/>
   <soapenv:Body>
	<urn:salesOrderList soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
	<sessionId>SESSION-ID</sessionId>
		<filters>
			<complex_filter>
				<filter>
					<key>Updated_at</key>
					<value>
						<key>gt</key>
						<value>2021-08-01 00:00:00</value>
					</value>
				</filter>
				<filter>
					<key>updated_at</key>
					<value>
						<key>lt</key>
						<value>2022-08-31 23:59:59</value>
					</value>
				</filter>
				<filter>
					<key>status</key>
					<value>
						<key>eq</key>
						<value>complete</value>
					</value>
				</filter>
			</complex_filter>
		</filters>
	</urn:salesOrderList>
</soapenv:Body>
</soapenv:Envelope>

Zum Schluss noch ein Beispiel zum Laden einer konkreten Bestellung über die incrementId = 100012345:

<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:Magento" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
 <soapenv:Header/>
   <soapenv:Body>
      <urn:salesOrderInfo soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
         <sessionId>SESSION-ID</sessionId>
         <orderIncrementId>100012345</orderIncrementId>
      </urn:salesOrderInfo>
   </soapenv:Body>
</soapenv:Envelope>

Magento 1.x Dokumentation und letzter Release

Der Support für Magento 1.x endete offiziell am 30.06.2020. Leider hat Adobe sich scheinbar entschieden alle Brücken zu Magento 1.x zu verbrennen und hat nicht nur die Online-Dokumentation sondern sogar die Repository auf GitHub gelöscht.

Gut, dass die Magento-Community groß und engagiert ist, sodass man die Online-Dokumentation hier weiterhin abrufen kann:

Die letzte offizielle Magento 1.x Version kannst du hier herunterladen:
Magento 1.9.4.5, Release: 04.05.2020

https://mage-one.com bietet unbegrenzten Support für Magento 1.x weiterhin an. Eine weitere Alternative ist der Fork https://www.openmage.org/ auf Basis von Magento 1.x.

DHL Intraship: Etiketten-Erstellung für Großbritannien deaktivieren

Seit dem 01.01.2021 ist Vereinigtes Königreich oder Großbritannien nicht mehr in der EU. DHL Intraship für Magento 1.x würde aber weiterhin versuchen, DHL-Etiketten zu erstellen. In den Lieferscheinkommentaren erscheint dann die folgende Fehlermeldung von DHL:

DHL Intraship::create::1101::Hard validation error occured. | hard validation error occured. | bitte geben sie die anzahl an. | bitte geben sie die beschreibung an. | bitte geben sie den warenwert an. | bitte geben sie das gewicht an. | bitte geben sie das gewicht an. | bitte geben sie die art der sendung an. | die angegebene art der sendung ist nicht gültig.

Um das zu verhindern, muss nur die Konfiguration der Erweiterung etwas angepasst werden.

Betroffene Datei:

app/code/community/Dhl/Intraship/etc/config.xml

Zeile 309:

<bpi>
  <code>BPI</code><countryCodes>BE,BG,DK,EE,FI,FR,GR,IE,IT,LV,LT,LU,MT,NL,AT,PL,PT,RO,SE,SK,SI,ES,CZ,HU,DB,CY,HR</countryCodes>
  <!-- default weight -->
  <weight>0.1</weight>
  <!-- partner ids -->
  <standard>01</standard>
  <express>02</express>
  <go-green>02</go-green>
  <go-green-express>02</go-green-express>
  <enabled-profiles>standard,go-green</enabled-profiles>
</bpi>
<countryCodesCustomsDeclaration>CH</countryCodesCustomsDeclaration>

Länderliste vorher:

BE,BG,DK,EE,FI,FR,GR,IE,IT,LV,LT,LU,MT,NL,AT,PL,PT,RO,SE,SK,SI,ES,CZ,HU,DB,CY,GB,HR

Länderliste nach der Anpassung:

BE,BG,DK,EE,FI,FR,GR,IE,IT,LV,LT,LU,MT,NL,AT,PL,PT,RO,SE,SK,SI,ES,CZ,HU,DB,CY,HR

Die Zeile 319 erscheint ebenfalls anpassungswürdig. Doch diese Anpassung ist nicht erforderlich, schadet aber auch nicht:

<countryCodesCustomsDeclaration>CH,GB</countryCodesCustomsDeclaration>

Das Ergebnis ist die Verweigerung der Erstellung von DHL-Etiketten:

SEPAGENTO: SEPA-Lastschrift für Magento 1.x mit Mandatsverwaltung

Das Projekt SEPAGENTO wurde am 31.12.2020 eingestellt.
Die letzte Version der Erweiterung für Magento 1.x kann kostenfrei inklusive der Dokumentation heruntergeladen werden.

Dokumentation:

Vorteile

  • IBAN/BIC-Validierung im Checkout
  • Online-Mandat-Erteilung über Magento-Bestellbedingung
  • SEPA-XML pain.008 (ISO 20022), DTA, CSV
  • Umfangreiche Konfigurationsmöglichkeiten
  • Hohe Kompatibilität u. a. mit Mage Setup und Magento DE
  • Sauberer Code, leitender Entwickler von Magento zertifiziert

Beschreibung

Pünktlich zur Einführung von SEPA (Single Euro Payments Area / Einheitlicher Euro-Zahlungsverkehrsraum) nach dem 2. Februar 2014 gibt es die Lösung von xonu EEC zur Abwicklung von SEPA-Lastschriften (Basislastschrift) über Magento-Online-Shops.

Der Kunde gibt IBAN und BIC im Checkout ein. Seine Eingaben werden bereits im Checkout normalisiert und validiert, sodass unnötige Fehlermeldungen dem Kunden erspart werden (der Kunde kann z. B. die IBAN/BIC mit und ohne Leerzeichen eingeben), und der Shop-Betreiber die Daten ohne weitere Aufbereitung und Validierung exportieren kann.

Die Erweiterung sendet keine Daten an Dritte. Die Validierung der IBAN/BIC findet auf Ihrem Server statt. Einzelne Validierungsschritte können in den Systemeinstellungen deaktiviert werden. Die IBAN/BIC können auf bestimmte Länder eingeschränkt werden. Die optionale BIC wird bereits jetzt unterstützt und kann ebenfalls auf bestimmte Länder eingeschränkt werden.

Das Online-Mandat verwaltet der Shop-Betreiber über die Bestellbedingungen von Magento. Das Lastschriftmandat wird vom Kunden im letzten Checkout-Schritt erteilt. Der Kunde und der Shop-Betreiber erhalten nach Bestellaufgabe eine E-Mail mit dem Mandat als Kopie. Der Shop-Betreiber kann den Inhalt der E-Mail über die Vorlagen bequem im Backend anpassen.

Optional kann die Erweiterung nach Bestellaufgabe die Rechnung automatisch erstellen und versenden. Die Lastschrift-Zahlungen können im Backend verwaltet und in dem SEPA-XML-Format exportiert werden. Auch die Export-Formate DTA und CSV stehen zur Verfügung.
Die Erweiterung überschreibt keine Core-Klassen und sorgt damit für höchstmögliche Kompatibilität. Sie wurde u.a. erfolgreich mit German Setup und Magento DE getestet.

Fontend- und Backend-Demo als Video

EBICS Online Banking

Funktionsweise

Einrichtung von EBICS Online Banking

Veraltete Session-Dateien von Magento automatisch löschen

Im Verzeichnis var/session produziert Magento unmengen von Dateien, die nie gelöscht werden. Nicht nur beim Einloggen, sondern bei jedem neuen Besuch des Online-Shops wird eine neue Session-Datei in diesem Verzeichnis angelegt.

Eine Session-Datei kann von 2 bis 200 kb groß sein. Nicht selten erreicht der Speicherverbrauch in diesem Verzeichnis Douzente und sogar Hunderte von Gigabytes. Deshalb sollten veraltete Session-Dateien automatisch gelöscht werden.

Automatische Löschung

Die folgende Anweisung löscht alle Session-Dateien älter als 90 Tage:

find var/session/ -mtime +90 -exec rm {} \;

Die Anweisung sollte als Cron-Job hinterlegt werden, um die Session-Dateien täglich zu löschen.

Erstmalige Bereinigung

Beim erstmaligen Bereinigen des Session-Verzeichnisses kann es einige Zeit dauern, bis die Ausführung abgeschlossen ist. Um eine realistische Erwartung an die Ausführungszeit zu haben, lohnt es sich deshalb den Speicherverbrauch vorher abzufragen:

du -h var/session

Damit die Konsole nicht blockiert wird, kann die Anweisung mit dem &-Operator am Ende der Zeile in ein Prozess ausgelagert werden:

find var/session/ -mtime +90 -exec rm {} \; &

Es wird eine Prozessnummer angezeigt. Die Ausführung der Anweisung kann über den Prozess-Manager htop nachvollzogen werden.

Fehlerbehebung

Verwendet man in der Anweisung oben var/session/* (mit Strernchen) statt var/session/ (ohne Sternchen) könnte beim erstmaligen Ausführen die folgende Fehlermeldung kommen:

Argument list too long

Das hängt damit zusammen, dass durch das Sternchen die gesamte Dateiliste als Parameter übergeben wird und die in ARG_MAX festgelegte Grenze überschritten wird. Man kann sich diese Zahl wie folgt anzeigen lassen:

getconf ARG_MAX

Die Lösung für das Problem ist das Entfernen von Sternchen, also var/session/ statt var/session/* zu verwenden.

Domain-Weiche in .htaccess

Wenn der Shop unter verschiedenen Top-Level-Domains erreichbar sein soll und Magento stets den richtigen Gültigkeitsbereich laden soll, kann die Domain-Weiche direkt in .htaccess umgesetzt werden statt die index.php zu bearbeiten.

Im folgenden Beispiel wird beim Aufruf des Shops unter xonu.de und www.xonu.de die StoreView mit dem code base und beim Aufruf von xonu.uk und www.xonu.uk entrsprechend StoreView mit dem code en geladen:

SetEnvIf Host www\.xonu\.de MAGE_RUN_CODE=base
SetEnvIf Host www\.xonu\.de MAGE_RUN_TYPE=store
SetEnvIf Host xonu\.de MAGE_RUN_CODE=base
SetEnvIf Host xonu\.de MAGE_RUN_TYPE=store

SetEnvIf Host www\.xonu\.uk MAGE_RUN_CODE=en
SetEnvIf Host www\.xonu\.uk MAGE_RUN_TYPE=store
SetEnvIf Host xonu\.uk MAGE_RUN_CODE=en
SetEnvIf Host xonu\.uk MAGE_RUN_TYPE=store

Wenn man sich mit regulären Ausdrücken wohl fühlt, lassen sich die Bedingungen auf vier Zeilen komprimieren:

SetEnvIf Host (www\.)?xonu\.de MAGE_RUN_CODE=base
SetEnvIf Host (www\.)?xonu\.de MAGE_RUN_TYPE=store

SetEnvIf Host (www\.)?xonu\.uk MAGE_RUN_CODE=en
SetEnvIf Host (www\.)?xonu\.uk MAGE_RUN_TYPE=store

Die in .htaccess gesetzten Umgebungsvariablen werden in der index.php in den letzten Zeilen ausgewertet:

/* Store or website code */
$mageRunCode = isset($_SERVER['MAGE_RUN_CODE']) ? $_SERVER['MAGE_RUN_CODE'] : '';

/* Run store or run website */
$mageRunType = isset($_SERVER['MAGE_RUN_TYPE']) ? $_SERVER['MAGE_RUN_TYPE'] : 'store';

Mage::run($mageRunCode, $mageRunType);

Aus der Bedingung in der index.php entnehmen wir, dass wir uns in der .htaccess nicht um „alle anderen Fälle“ kümmern müssen. Wird der Shop unter einer anderen Domain aufgerufen, die in den Bedingungen nicht berücksichtigt wurde, wird der Standard-StoreView geladen.

Davon ausgehend, dass der deutschsprachige StoreView mit dem Code base standardmäßig geladen wird (und pro Zeile mehrere Variablen gesetzt werden können), lässt sich die Domain-Weiche auf eine Zeile reduzieren:

SetEnvIf Host (www\.)?gaiwan\.(co\.)?uk MAGE_RUN_CODE=uk MAGE_RUN_TYPE=store

 

dsn=5.0.0, stat=Service unavailable, Magento-Emails kommen nicht an

Fehlerbeschreibung

Nach einem Server-Umzug kommen Emails, die Magento versendet sehr oft nicht an. Im mail.log findet man Einträge wieder diesen:

relay=mx0.... [...], dsn=5.0.0, stat=Service unavailable

Oder ganz spezifisch:

relay=mx0.webpack.hosteurope.de. [80.237.138.5], dsn=5.0.0, stat=Service unavailable

Bei diesem Beispiel wurden Emails, die an Mail-Server gehostet von HostEurope gesendet wurden abgelehnt.

Ursache

Return-path im Email-Header war nicht gesetzt. Der Zielserver hat daraufhin aus Sicherheitsgründen die Email abgelehnt.

Vor dem Server-Umzug war der Return-Path global in der Postfix-Konfiguration gesetzt, sodass die Einstellung auf der Anwendungsebene (s. Lösung) keine Rolle gespielt hat.

Lösung

Die Lösung ist denkbar einfach, in dem das Setzen des Return-path in System > Konfiguration > Erweitert: System > Mailversand Einstellungen aktiviert wird (Antwortadresse eintragen = Ja):

 

Weiterlesen:

Kontaktformular mit Checkbox (DSGVO-Schutz)

Ob nach der DSGVO ein Kontaktformular eine Checkbox nun unbedingt braucht oder nicht geht es in diesem Artikel nicht. Entscheidet sich der Shop-Betreiber für die Checkbox, soll hier gezeigt werden, wie diese Checkbox durch Nutzung vorhandener JS-/CSS-Biblitheken von Magento am elegantesten und einfachsten eingebaut werden kann:

Als Vorlage nutzen wir hier die Template-Datei app/design/frontend/base/default/template/contacts/form.phtml, die wir in das eigene Theme-Verzeichnis kopieren und dort bearbeiten.

Die Felder des Kontaktformulars sind als li-Elemente organisiert. Wir fügen nun ein neues li-Element mit der Checkbox hinzu:

<li>
    <li class="control" style="max-width: 450px;">
        <label style="float:right" for="gdpr_consent" class="required">Sie erklären sich damit einverstanden, dass Ihre Daten zur Bearbeitung Ihres Anliegens verwendet werden. Weitere Informationen und Widerrufshinweise finden Sie in der <a href="#">Datenschutzerklärung</a>.</label>
        <input type="checkbox" name="gdpr_consent" value="1" class="checkbox required-entry"/>
    </li>
</li>

Die Checkbox wird zum Pflichtfeld durch zwei Bedingungen: value=“1″ und class=“… required-entry“. Die Validierung wird dann durch die Logik in js\prototype\validation.js durchgeführt: Die Checkbox überträgt den Wert 1 nur wenn ausgewählt und muss deshalb bestätigt sein, damit das Formular gesendet werden kann.

Die Angabe max-width: 450px; berücksichtigt CSS von RWD-Theme, welches das Formular responsive macht. Danach wird die maximale Breite des Nachrichtenfeldes auf 450 Pixel gesetzt. Dementsprechend soll auch die Breite der Checkbox mit dem langen Text begrenzt sein.

Nun können wir den Text noch in den Static Block mit dem Bezeichner gdpr_consent_contact auslagern:

<li>
    <li class="control" style="max-width: 450px">
        <label style="float:right" for="gdpr_consent" class="required"><?php echo $this->getLayout()->createBlock('cms/block')->setBlockId('gdpr_consent_contact')->toHtml() ?></label>
        <input type="checkbox" name="gdpr_consent" value="1" class="checkbox required-entry"/>
    </li>
</li>

Modifizierte Template-Datei als Download: form.phtml.zip

Weiterlesen:

DSGVO-Schutz für Magento

Am 25. Mai 2018 ist die neue DSGVO direkt anwendbar, doch die neuen Regeln wurden schon vor 2 Jahren eingeführt. Es wurde ordentlich Panik verbreitet. Es gibt kostenpflichtige Produkte und Abonnements, die sicherlich ihre Abnehmer gefunden haben.

Für den DSGVO-Schutz im Frontend braucht man mit Sicherheit keinen kostenpflichtigen Cookie-Dienst zu abonnieren. Hier geht es zum kostenfreien Cookie-Hinweis, mit verschiedenen Designs, bestens anpassbar und komplett open source: https://cookieconsent.insites.com

Auch die DSGVO-konforme Rechtstexte können mit Hilfe kostenfreier Dienste zusammengestellt werden: https://shop.trustedshops.com/de/rechtstexte/

Bei Verwendung eines Generators das Ergebnis vor der Veröffentlichung unbedingt durchlesen und gegebenfalls vervollständigen. Um ganz sicher zu gehen, sollte man einen Anwalt zur Rate ziehen, sofern man kurzfristig noch einen findet.

Es gibt sicherlich noch mehr Änderungen, die die DSGVO mit sich bringt. Bei Händlerbund gibt es eine Zusammenfassung mit Vorlagen und Mustern, z.B. für das Verarbeitungsverzeichnis.

Synchronisierung von Magento und Amazon mit M2EPro: Wie werden Änderungen der Produktdaten erkannt?

M2EPro ist eine sehr umfangreiche Erweiterung für die Verbindung von Magento mit Amazon (und eBay). Bei der Synchronisierung kann es zu Schwierigkeiten kommen, sodass es interessant mal etwas hinter der Kulissen der Erweiterung zu blicken und mehr Möglichkeiten zu bekommen, die Synchronisierung zu steuern und ggf. einzugreifen. Hier sind einige Erkenntnisse rund um Synchronisierungslogik der Erweiterung zusammengetragen.

  • Welche Daten bei der Synchronisierung übertragen wurden kann unter Amazon > Logs > Listings nachvollzogen werden.
  • Die Synchronisierung wird automatisch mit Cron ausgeführt.
    • Der Zustand der Synchronisierung wird unter Amazon > Konfiguraton > Synchronisierung angezeigt: Wenn man diesen Bereich betritt wird automatisch ein Fortschrittsbalken geladen, sofern die Synchronisierung läuft. Ist der Balken nicht zu sehen, läuft aktuell keine Synchronisierung.
    • Es ist nicht erforderlich das Browserfenster offen zu halten. Die Synchronisierung läuft im Hintergrund.
    • Auch wenn die Synchronisierung manuell gestartet wird, läuft sie im Hintergrund weiter, auch wenn das Browserfenster geschlosen wird.
  •  Es kann sein, dass die Synchronisierung nicht mehr endet.
    • Im Backend gibt es keine Möglichkeit die Synchronisierung abzubrechen.
    • Ein vielleicht nicht ganz sauberer Weg ist über den direkten Eingriff in die Datenbank. Durch Löschen der Zeilen in m2epro_lock_item wird die Synchronisierung gestoppt:
      DELETE FROM m2epro_lock_item;
    • Doch wenn M2EPro Änderungen der Produktdaten auf der Liste hat, wird die Synchronisierung demnächst neu gestartet bzw. fortgesetzt.
  • M2EPro merkt welche Produktdaten geändert wurden über Einträge in der Tabelle m2epro_product_change.
    • Für ein Produkt werden mehrere Zeilen angelegt, jeweils eine pro Attribut. In der Zeile ist dann der alte und der neue Wert, sowie Attributname gespeichert.
    • Durch Löschen der Einträge in dieser Tabelle kann die Synchronisierung wirdklich angehalten und zurückgesetzt werden.
  • Für die Eintragung der Änderungen sind in M2EPro Observer definiert.
    • Änderungen der Produktdaten durch direkte Datenbankimporte können mit einer entsprechenden Option erkannt werden. Dies kann jedoch dazu führen, dass mehr Daten synchronisiert werden als nötig, sodass das Ergebnis der Synchronisierung auf sich länger warten lässt.
    • Für MAGMI gibt es von M2EPro ein offizielles Modul, der die Tabelle mit entsprechenden Daten befüllt.

 

Weiterlesen:

Wo hat der neue Feuerkäfer die Kekse versteckt?

Nach dem FireBug update wird der eine oder andere plözlich den Cookie-Reiter vermissen. Dazu muss in den Optionen der Reiter Speicher aktiviert werden:

In meinem Fall war bereits ein solcher Reiter zu sehen, nach der aktivierung gibt es zwei gleichnamige Reiter. Die Cookies sind in dem rechten versteckt:

Empfehlung: Guter Tee passt immer gut zu Keksen 🙂

 

CSS-Style auf einer Produktseite einbinden

Möchte man eine Produktseite etwas umgestalten, lässt sich CSS über das Layout-Update in den Kopfbereich einfügen. Ohne eine zusätzliche CSS-Datei herunterladen zu müssen, können wir direkt im Layout-XML unsere CSS-Regeln definieren:

<reference name="head">
  <block type="core/text" name="style">
    <action method="setText">
      <text><![CDATA[<style>h1 {color:red}</style>]]></text>
    </action>
  </block>
</reference>

 

Weiterlesen:

Update auf Magento 1.9.2.3 oder SUPEE-7405: Bestelldetails laden nicht

Fehlerbeschreibung

Nach Update von Magento auf 1.9.2.3 oder Installation des Sicherheitspatches SUPEE-7405 wird die Bestellung in Detailansicht nicht geladen. Man sieht im Backend die folgende Ausgabe:

bestelldetails-magento-500-server-error

Mit FireBug lässt sich außerdem Server-Fehler 500 nachvollziehen.

Fehlerursache

Seit PHP 5.4 wurde ein neuer Array-Syntax eingeführt. Wird PHP Version unter 5.4 verwendet, kommt es zu diesem Fehler. Seit PHP 5.4 kann der Code $a = array(1, 2, 3) alternativ kürzer gefasst werden: $a = [1, 2, 3].

Fehlerbehebung

Um sich das Update von PHP  zu sparen, kann in Mage_Adminhtml_Helper_Sales (Datei: app/code/core/Mage/Adminhtml/Helper/Sales.php) in Zeile 124 die folgende Zeile:

$links = [];

durch die folgende ersetzt werden:

$links = array();

Weiterlesen:

Installation von Magento 2.0: Erster Eindruck

Seit dem 17.11. ist Magento 2.0 in der finalen Version raus. Die Kommunikation im Downloadbereich ist deutlich: Magento 1.x ist im Tab „Release Archive“ zu finden:

magento1-archive

Der Installationsassistent ist freundlich aber deutlich: Magento 2 erfordert mindestens PHP 5.5 und MySQL 5.6, sodass viele Testumgebungen in den sich Magento 1.x noch funktionierte ohne Update unbrauchbar sind.

Der aktuelle XAMPP erfüllt die Anforderungen von Magento 2. Auch die PHP-Extensions (curl, dom, iconv, mcrypt, spl, xsl, intl, mbstring, ctype, hash, openssl, simplexml, xmlwriter, gd) sind alle enthalten.

magento2-system-requirementsPHP-Extensions intl muss nur noch in der php.ini aktiviert werden, im Verzeichnis ext ist die Datei php_intl.dll bereits enthalten. Nach dem Setzen von always_populate_raw_post_data = -1 in der php.ini sind alle Kriterien erfüllt:

magento2-ready-to-install

Da wir in Zukunft Magento auch unterwegs vom Handy aus installieren wollen, ist schon der Installationsassistent responsive:

magento2-ready-to-install-responsiveIm aktuellen XAMPP wird übrigens MariaDB 10.1.8 statt MySQL 5.6 mitgeliefert. MariaDB ist der binäre Ersatz für MySQL, d. h. alle binäre Dateien von MariaDB heißen mit denen von MySQL identisch, sodass alle Werkzeuge und Skripte für MySQL weiterhin funktioneren werden. Die MySQL Workbench würdigt MariaDB mit der folgenden Meldung, funktioniert aber trotzdem (vermutlich liegt es nur an der Versionsnummer 10.1.8 statt 5.6, die im Protokoll übermittelt wird):

xampp-maria-db-workbench

Der Installationsassistent von Magento 2 schlägt von sich aus eine kryptische URL für den Administrationsbereich vor:

magento2-admin-path

Unsere Zeitzone heißt anders, und man kann vorab die Ausgabe einzelner Module deaktivieren:

magento2-installation-zeitzone

Zusätzlich zu der kryptische Admin-URL wird noch auf ein schwaches Passwort hingewiesen:

magento-passwort-staerke

Die Datenbank wird nun nicht mehr im Hintergrund aufgebaut, sondern mit einer Fortschrittsanzeige und der Option es im Fehlerfall erneut zu versuchen:

magento2-db-installation-fortschritt-fehler

In diesem Fall ist der Speicher des Laufewerks vollgelaufen, auf dem die XAMPP-Testumgebung gerade läuft. Nach Speicherfreigabe und dem Klick auf Try Again wird der Fortschrittsbalken mit einer Animation zurückgespullt 🙂

magento2-installation-erfolgreich

So sieht das neue Login-Formular aus:

admin-loginUnd so sieht es im Backend aus:

magento2-backend

FireBug zeigt, dass diverse Schriftarten für die Menü-Icons nicht geladen wurden:

magento2-schriftartenDer Ordner pub/static/adminhtml/Magento/backend/en_US/fonts/opensans/bold ist tatsächlich leer. Magento 2 kopiert statische Dateien in öffentlich zugängliche Ordner bei Bedarf. In diesem Fall hat diese Logik anscheinend versagt, und wir finden sicherlich später warum.

Erklärung des Google Doodle zum 200. Geburtstag von George Boole

google-doodleBool’sche Algebra ist in der Programmierung nicht mehr wegzudenken. Heute feiert Google den 200. Geburtstag von George Boole, der die Bool’sche Algebra erfunden hat.

Während im Alltag uns die mathematischen Operationen wie Addition und Multiplikation eher begegnen, sind boolsche Operationen in der Programmierwelt der Alltag. Google demonstriert die Rechenergebnisse von vier Operatoren: AND, OR, XOR, NOT. Ähnlich wie 2 +3 = 5 ist, ist 1 OR 0 = 1. Bool’sche Operatoren rechnen mit binären Zahlen 1 (wahr, engl. true) und 0 (falsch, engl. false). Das Ergebnis einer Bool’schen Operation ist ebenfalls eine binäre Zahl.

Der Buchstabe g im Logo zeigt die Eingabe, die bestimmt, welche anderen Buchstaben leuchten (d. h. mit Farbe gefüllt werden). Ein leuchtender Buchstabe entspricht der binären 1 oder dem logischen Wert Wahr, während ein grauer Buchstabe der 0 entspricht, oder dem logischen Wert Falsch. Wird der x bzw. y eingeblendet, so ist x bzw. y wahr, d. h. gleich 1. Wird x bzw. y ausgeblendet, so ist x bzw. y falsch oder gleich 0. Die Animation des Logos wechselt die vier Kombinationen von x und y ab und zeigt in den anderen Buchstaben die Ergebnisse des jeweiligen Operators.

Intuitive Erklärung der Operatoren:

  • AND: Beides muss wahr sein, x und y müssen beide wahr sein.
  • OR: Mindestens eins von beiden x oder y muss wahr sein.
  • XOR: Genau eins x oder y muss wahr sein, aber nicht beide x und y.
  • NOT: Der umgekehrte Wert, die Negation, nicht wahr ist falsch, nicht 0 ist 1.

Die nachfolgende Wahrheitstabelle fasst alle vier Kombinationen zusammen:

G o o g l e
AND XOR OR x y NOT y NOT x
0 0 0 0 0 1 1
0 1 1 0 1 0 1
0 1 1 1 0 1 0
1 0 1 1 1 0 0

Auf der Seite mit Suchergebnissen ist die Animation nicht mehr zu sehen. Dort setzt Google das Thema mit dem &-Zeichen vor dem Buchstaben g fort:

google-mini-bool

Weiterlesen:

Static Blocks Everywhere: Keine Ausgabe nach Installation von SUPEE-6788

Static Blocks Everywhere auf Magento Connect

Fehlerbeschreibung

Kurz nach der Veröffentlichung des Magento-Patch SUPEE-6788 und Magento-Update haben wir mehrere Anfragen bekommen, dass die Ausgabe für statische Blöcke in Zusammenhang mit der Erweiterung Static Blocks Everywhere nicht mehr funktioniert.

Ursache

Das Problem hängt mit der Verfeinerung des Rechtesystems in Magento zusammen.

Lösung

Zur Behebung des Problems muss unter System > Berechtigungen > Blocks ein neuer Block angelegt werden:

system-berechtigungen-blocks

Der Blockname ist cms/block, Is Allowed wird auf Yes/Ja gesetzt:

cms-block-berechtigungNach dem Speichern des neuen Blocks sieht die Liste der Block-Berechtigungen wie folgt aus:

magento-berechtigungen-blocks

Siehe auch:

MySQL: Job failed to start, mysqld: Can’t create test file user.lower-test

Fehlerbeschreibung

Nach dem Verschieben des MySQL-Datenverzeichnisses (Parameter datadir in /etc/mysql/my.cnf) startet der MySQL-Server nicht mehr. Als Fehler wird Job failed to start gemeldet:

root@ubuntu:~# /etc/init.d/mysql restart
Rather than invoking init scripts through /etc/init.d, use the service(8)
utility, e.g. service mysql restart

Since the script you are attempting to invoke has been converted to an
Upstart job, you may also use the stop(8) and then start(8) utilities,
e.g. stop mysql ; start mysql. The restart(8) utility is also available.
start: Job failed to start

Der MySQL-Server-Start direkt aus dem bin-Verzeichnis endet mit dem Fehler Can’t create test file /…./user.lower-test:

root@ubuntu:/bin# mysqld
150831  0:40:58 [Warning] Can't create test file /..../user.lower-test
150831  0:40:58 [Warning] Can't create test file /..../user.lower-test

Mögliche Ursache

Bei Ubuntu ist AppArmor eine häufige Ursache. Diese Sicherheitssoftware enthält ein Profil für MySQL, in dem u. a. das Datenverzeichnis von MySQL festgelegt ist. AppArmor blockiert den Zugriff von MySQL, weil das neue Datenverzeichnis nicht bekannt ist.

Lösung

Durch Eintrag des neuen Daten-Verzeichnisses in /etc/apparmor.d/usr.sbin.mysqld kann der Fehler behoben werden:

vi /etc/apparmor.d/usr.sbin.mysqld

...
  # /var/lib/mysql/ r,
  # /var/lib/mysql/** rwk,
  /neues-datenverzeichnis/mysql/ r,
  /neues-datenverzeichnis/mysql/** rwk,
...

Nach dem Neustart von AppArmor sollte auch MySQL-Server nun starten:

/etc/init.d/apparmor restart
/etc/init.d/mysql restart

Magento-Marktanteil in 2015

Entwicklung seit 2012

Update von der Meet Magento 2015

Auf der diesjährigen Meet Magento hat uns Thomas Goletz (Vorstand der Netresearch App Factory AG) gab es einige Updates zu den Statistiken rund um die Magento-Infrastruktur:

  • Mehr als 240.000 Installationen
  • Mehr als 7.500 bekannte Erweiterungen
  • Über 7 Jahre erfolgreiches Magento 1.x

Marktanteile von Magento nach Wappalyzer

Nach Wappalyzer (ein Browser-Plug-In, der die auf der Webseite verwendete Software basierend auf dem Quellcode u. a. Merkmalen erkennt) ist der Marktanteil von Magento auf 19% weiter gesunken:

Absolute Zahlen der Top 10 Online-Shop-Systeme:

Die Spalte „Detections“ zeigt indirekt die Besucherzahlen und damit die Popularität einzelner Shops, die das jeweilige System verwenden.

Zum Vergleich: 2012 hat Wappalyzer bei 25% aller untersuchten Online-Shops Magento erkannt. 2014 waren es 22%, und heute noch 19%.

Nach dieser Quelle ist WooCommerce (WordPress-basierte Shops) der deutliche Gewinner mit einem Anstieg von 15% im Vergleich zum Vorjahr. Ob WooCommerce eine niedrige Einstiegshürde bietet und sich deshalb gut verbreitet oder ob Wappalyzer seine Erkennungsalgoritmen verbessert und die Statistik damit verzerrt, hat kann man nur vermuten.

Magento-Marktanteile nach Google Trends, Search Insights

Das Suchvolumen weltweit zeigt Magento als klaren Sieger mit stabiler Nachfrage:

In Deutschland gewinnt Magento mit deutlichem Abstand:

Während in den USA Shopify Magento überholt hat:

Up-Selling-Produkte werden nicht gespeichert

Fehlerbeschreibung

Nach der Auwahl von Up-Sell-Produkten im Backend wird nach dem Speichern des Hauptprodukts die Liste zurückgesetzt.

Fehlerursache

Der Fehler tritt auf, wenn die Erweiterung BL_CustomGrid (Enhanced Admin Grid auf Magento Connect und GitHub).

Fehlerbehebung

Der Fehler kann dadurch behoben werden, dass die Erweiterung speziell für die Tabelle der Up-Sell-Produkte deaktiviert wird. Die Erweiterung stellt dazu selbst die entsprechende Mittel zur Verfügung.

In Grid Anpassung > Grid Infos zur Up-Sell-Tabelle kann der Blocktyp herausgefunden werden:

In System > Konfiguration > Enhanced Admin Grids: Konfiguration Basis wird der Blocktyp adminhtml/catalog_product_edit_tab_upsell als Ausnahme hinzugefügt:

Danach kann zwar die Tabelle für Up-Sell-Produkte nicht mehr angepasst werden, doch das Speichern der Auwahl funktioniert.

Magento 1.9.1 sendet keine E-Mails

Seit Magento 1.9.1 wurden bei den Transaktions-E-Mails zwei wichtige Änderungen eingeführt: Responsive E-Mail-Vorlagen und E-Mail-Warteschlange (E-Mail-Queue). Mit dem ersten Shop auf Basis von Magento 1.9.1, wird man sich zunächst wundern, dass die Bestellbestätigung nicht sofort oder gar nicht nach Abschluss der Bestellung versendet wird, zumindest bis der Cron-Skript von Magento aufgerufen wurde.

Hinweis: Ein weiterer Grund, dass eine E-Mail nicht versendet wird könnte ein fehlendes E-Mail-Template oder ein Fehler im verwendeten Template sein.

Die E-Mail-Queue beschleundigt den Bestellvorgang, da die Bestellbestätigung im separaten Prozess versendet wird, und nicht wie in Vorgängerversionen, als Teilvorgang der Aufnahme einer neuen Bestellung. Dies ist Performance Optimierung, die sich besonders für Shops mit vielen Bestellabschlüssen lohnt. Mit der Magento-Erweiterung AOE_Scheduler lässt sich der Magento-interne Cron-Job der E-Mail-Queue gut veranschaulichen:

Die letzte Version dieser Erweiterung auf Magento Connect ist 0.3.2 und offiziell bis Magento 1.7 freigegeben, funktioniert jedoch mit Magento 1.9.1 ebenfalls einwandfrei. Die aktuelle Version 0.4.3 kann auf GitHub heruntergeladen werden.

Die Vorschaufunktion für E-Mail-Vorlagen ist in Magento ohnehin unbrauchbar, da sie die Vorschau ohne Kunden- oder Beispieldaten anzeigt, sodass das Endergebnis erst mit einer Testbestellung kontrolliert werden kann:

Bei der Einrichtung von E-Mail-Templates in einem neuen Shop wird die E-Mail-Queue die Arbeit allerdings erschweren, da die Email nicht sofort nach Abschluss einer Testbestellung ankommt, sodass bei der Kontrolle einer jeden Anpassung neben dem Aufwand einer Testbestellung noch eine zusätzliche Wartezeit entsteht.

Eine Abhilfe hierbei schafft die Magento-Erweiterung Modulwerft_EmailManager, die eine sofortige Vorschau bzw. Anzeige einer jeden von Magento vorbereiteten oder sofort versendeten E-Mail direkt im Backend ermöglicht.

Im Kommentarverlauf einer Bestellung kann die Bestellbestätigung, die an den Kunden versendet wurde oder wird, sofort augerufen werden:

An der Anzeige Versand ausstehend ist sofort erkennbar, dass die E-Mail noch nicht an den Kunden versendet wurde, sondern sich in der E-Mail-Warteschlange befindet. Der Klick auf den gelb hinterlegten Bereich zeigt die vorbereitete E-Mail so wie sie beim Kunden ankommen wird:

Neben der praktischen Anzeigefunktion der noch nicht versendeten E-Mails in Magento 1.9.1 überwacht und speichert der E-Mail-Manager alle E-Mails, die von Magento generiert und versendet wurden oder werden.

Dem Kundendienst wird diese Erweiterung helfen schnell nachzuvollziehen, welche E-Mails an den Kunden versendet wurden. Bei registrierten Kunden können die E-Mails in Kundendetails angezeigt und absteigend nach der Zeit sortiert werden:

In System > E-Mail-Log bekommt man die Übersicht über alle von Magento versendeten E-Mails und kann sie nach Typ (z. B. Bestellbestätigung, Rechnung, Lieferschein) Empfänger, Betreff und einigen weiteren Kriterien filtern. Nach dem Empfänger gefiltert können auch für Gastkunden alle versendeten E-Mails gefunden werden.

DHL-Label-Drucker und Etiketten-Format

DHL-Versand-Etiketten können entweder mit einem beliebigen Drucker gedruckt werden, der auch Papier im A5-Format bedrucken kann, oder mit einem Thermodrucker.

Etiketten-Drucker für DHL-Intraship

Von DHL wird der B-EV4T-G von Toshiba TEC empfohlen (vollständige Modellbezeichnung: B-EV4T-GS14-QM-R):

Der Anschaffungspreis beträgt inkl. MwSt. ca. 250,- EUR. Die passenden Thermo-Etiketten werden von DHL dem Kunden kostenfrei zugeschickt. Passende Etiketten können im DHL-Geschäftskundenportal günstig erworben werden.

Der Drucker kann sowohl mit einer Etikettenrolle als auch mit gestapelten Etiketten verwendet werden, wie sie von DHL geliefert werden. Die Etiketten bleiben dann außerhalb des Druckers im Karton liegen und werden durch den hinteren Schlitz eingezogen. Da die Thermo-Etiketten von DHL die schwarze Druckfarbe bereits im Papier enthalten, wird das teure Thermotransfer-Farbband nicht benötigt.

Der Vollständigkeit halber soll noch der große und ca. 700,- € (inkl. MwSt.) teuere Drucker B-EX4D2 von Toshiba erwähnt werden, der von der DHL bei 10.000 Sendungen pro Tag empfohlen wird. Der „kleine“ B-EV4T-G wird für viele Shop-Betreiber schnell genug sein: Für den Druck eines Etiketts (Format: s. u.) braucht er nur 2 Sekunden!

Installation des B-EV4T-G

Obwohl der Drucker einen Netzwerk-Anschluss hat und offiziell das Drucken über LAN unterstützen soll, haben wir es nicht geschafft ihn mit einem Windows-Rechner über LAN anzusprechen und betreiben ihn deshalb über USB.

In der eigenen Installationsanleitung schlägt DHL einen abgewandelten Druckertreiber vor: https://de.toshibatec.eu/dhl-printout/

Dieser funktioniert einwandfrei, genau wie der offizielle Drucker-Treiber von Toshiba, jedoch wird das richtige DHL-Etikettenformat bereits vorinstalliert:

Die Testseite kann in den Druckereinstellungen gedruckt werden:

Gleich nach der Installation, ohne weitere Einstellungen, müsste der Drucker die Testseite erfolgreich ausgeben und dabei genau auf der Perforationslinie des nächsten Etiketts anhalten.

DHL-Etiketten-Format und DHL-Intraship in Magento

Wenn der offizielle Treiber von Toshiba bereits installiert wurde, kann das DHL-Etikettenformat (101,6 mm x 199,0 mm) manuell eingegeben werden:

In den Format-Einstellungen der Magento-Erweiterung DHL Intraship (die o. W. direkt mit dem neuen „DHL Versenden“ funktioniert) wird das passende Etiketten-Format nicht angeboten.

Für die Ränder können negative Abstände angegeben werden, um das perfekte Druckergebnis zu erzielen:

  • Papierformat der Etiketten: A5
  • Linker Rand: -53 mm
  • Oberer Rand: -49 mm

Eingaben der Abstände ohne Einheit in System > Konfiguration > Verkäufe: DHL > Etiketten-Einstellungen:

Das Ergebnis als PDF-Ausgabe liefert ein im linken oberen Rand des A5-Formats positionierten DHL-Etikett, der dann perfekt mittig aus dem Thermodrucker (B-EV4T-G) kommt:

SQL-Fehler in Magento debuggen: Vollständige SQL-Abfrage im Stack Trace anzeigen

Bei einem SQL-Fehler kann der Stack Trace von Magento in der Fehlerausgabe wie folgt aussehen:

#0 lib/Zend/Db/Statement/Pdo.php(228): PDOStatement->execute(Array)
#1 lib/Varien/Db/Statement/Pdo/Mysql.php(110): Zend_Db_Statement_Pdo->_execute(Array)
#2 lib/Zend/Db/Statement.php(300): Varien_Db_Statement_Pdo_Mysql->_execute(Array)
#3 lib/Zend/Db/Adapter/Abstract.php(479): Zend_Db_Statement->execute(Array)
#4 lib/Zend/Db/Adapter/Pdo/Abstract.php(238): Zend_Db_Adapter_Abstract->query('SELECT `a`.* FR...', Array)
#5 lib/Varien/Db/Adapter/Pdo/Mysql.php(419): Zend_Db_Adapter_Pdo_Abstract->query('SELECT `a`.* FR...', Array)
#6 lib/Zend/Db/Adapter/Abstract.php(825): Varien_Db_Adapter_Pdo_Mysql->query(Object(Varien_Db_Select), Array)

Die Information ist begrenzt hilfreich, da die Abfrage „SELECT `a`.* FR…“, die zu dem Fehler geführt hat, nicht ausgeschrieben wird.

Um die fehlerhafte Abfrage analysieren zu können, kann der Debug-Modus aktiviert werden, in dem die Variable $_debug in Varien_Db_Adapter_Pdo_Mysql auf true gesetzt wird. Die SQL-Abfrage wird dann in $_debugFile = ‚var/debug/pdo_mysql.log‘ gespeichert und kann weiter analysiert werden.

Rechtskonforme SEPA-Lastschrift für Magento mit Mandat-Verwaltung und SEPA-XML-Export (ISO 20022)

Seit heute steht die neue Version 2.0 der SEPA-Lastschrift-Erweiterung für Magento zur Verfügung:

Die neue Version erfüllt alle rechtlichen Vorschriften, die vom Gesetzgeber gefordert werden. Diese Anforderungen haben wir weiter unten für Sie zusammengefasst. Für die neue Version der Erweiterung stellen wir eine ausführliche Dokumentation und eine neue Live-Demo bereit.

Vielen Dank an alle Kunden und Bankmitarbeiter, die uns Verbesserungsvorschläge und rechtliche Hinweise zugeschickt haben und uns so bei der Verbesserung der SEPA-Lastschrift für Magento unterstützt haben!

Zusammenfassung der neuen Regeln für SEPA-Lastschrift

Im Zuge der Einführung der neuen SEPA-Lastschrift und der Abschaffung der nationalen Lastschrift-Verfahren, wie beispielsweise der deutschen Lastschrift, hat es Regeländerungen gegeben. Diese Regeländerungen sollten seitens des Online-Händlers unbedingt beachtet werden, da dieser sonst Gefahr läuft Lastschriften nicht regelkonform einzuziehen und damit die Frist für die Rückholung des Lastschriftbetrages seitens des Kunden zu verlängern.

Die nachfolgend aufgeführten Änderungen sind mit der Einführung der SEPA-Lastschrift in Kraft getreten. Die neue Version unserer SEPA-Lastschrift-Erweiterung für Magento unterstützt und automatisiert diese neuen Regelungen:

  • IBAN und BIC Angabe
    Die Erweiterung prüft die eingegebene IBAN und BIC auf Fehler und zeigt diese an. Die Fehlerquote bei der Eingabe des langen IBAN-Codes wird dadurch minimiert. Für bestimmte Länder kann BIC als optionale Eingabe eingestellt werden.
  • Verwaltung von SEPA-Lastschriftmandaten
    Zusätzlich zum Versenden des erteilten SEPA-Lastschriftmandats per Email werden alle Daten des Mandates und das Dokument in der Shop-Datenbank gespeichert. Registrierte Shop-Kunden können Lastschriftmandate für wiederkehrende Zahlung erteilen und sie bei nächster Bestellung nutzen ohne die Bankverbindung erneut einzugeben. Das Mandat verfällt automatisch 36 Monate nach Nichtnutzung.
  • Rechtskonforme Vorlagen für SEPA-Lastschriftmandate
    Mit der Erweiterung sind sorgfältig recherchierte Vorlagen nach Vorgaben des European Payment Council (EPC) und der Deutschen Kreditwirtschaft (DK) in deutscher und englischer Sprachen enthalten: SEPA-Lastschriftmandat für einmalige und wiederkehrende Zahlung und Vorabinformation. Auf den vom Schuldner abweichenden Kontoinhaber wird gesondern hingewiesen.
  • Vorabinformation (Pre-Notification)
    Die Erweiterung bietet die Möglichkeit den Hinweis auf Verkürzung der Frist für Vorabinformation im Checkout anzuzeigen und die Vorabinformation in der Bestellbestätigung anzuzeigen.
  • Automatischer SEPA-XML-Export nach ISO 20022
    Neben dem manuellen Export im Backend ist automatischer Export von Zahlungen im standardisierten SEPA-XML-Format möglich.

SEPA-Lastschrift für Magento auf Magento Connect
Wir laden Sie ein, die neuen Funktionen in unserer Live-Demo auszuprobieren!

WSDLSOAP-ERROR: Parsing WSDL: Couldn’t load from … : failed to load external entity …

Fehlerbeschreibung

$client = new SoapClient('http://beispiel.de/index.php/api/v2_soap/?wsdl');
$sessionId = $client->login('benutzer', 'geheim');

Beim Versuch eine API-Verbindung mit Magento herzustellen, wird von SoapClient der folgende Fehler gemeldet:

WSDLSOAP-ERROR: Parsing WSDL: Couldn't load from ... : failed to load external entity ...

Mögliche Ursache

Der Server kann seinen eigenen Namen nicht auflösen. Als Bestätigung dieser Ursache, kann der Apache-Server neu gestartet werden:

/etc/init.d/apache2 restart

Wird beim (erfolgreichen) Start von Apache die nachfolgende Warnung ausgegeben, liegt die o. g. Ursache vor:

Could not reliably determine the server's fully qualified domain name

Lösung

An Stelle der Domain kann in der API-URL die IP des Servers verwendet werden:

$client = new SoapClient('http://123.45.67.89/index.php/api/v2_soap/?wsdl');
$sessionId = $client->login('benutzer', 'geheim');

Die bessere und sauberere Lösung wäre der Eintrag der Domain in der hosts-Datei auf dem Server (die Datei befindet sich unter /etc/hosts):

123.45.67.89      beispiel.de

Anschließend sollte Apache neu gestartet werden.

Weiterlesen:

SoapClient: looks like we got no XML document

Fehlerbeschreibung

Beim Versuch sich über Magento-API einzuloggen, wird folgender Fehler gemeldet:

SoapClient: looks like we got no XML document

Fehleranalyse

Die Fehlermeldung ist viel zu allgemein. Deshalb sollte die Serverantwort genauer analysiert werden, die angeblich kein (gültiges) XML-Dokument ist. Um die empfangenen Daten analyseren zu können, wird die Abfragemethode von SoapClient überschrieben:

class SoapClientDebug extends \SoapClient{

    public function __doRequest($request, $location, $action, $version = SOAP_1_1, $one_way = 0){

        $xml = explode("\r\n", parent::__doRequest($request, $location, $action, $version, $one_way = 0));

        print_r($xml);
        print '<br/><br/>'.str_repeat('#', 100).'<br/><br/>';

        if(isset($xml[5])) return $xml[5];
        return '';
    }

}

Die API-Kommunikation läuft nun über die überschriebene Klasse:

// $client = new SoapClient('http://beispiel.de/index.php/api/v2_soap/?wsdl');
$client = new SoapClientDebug('http://beispiel.de/index.php/api/v2_soap/?wsdl');

Mögliche Ursachen und Lösungen

In unserem Fall hat der Server in mehreren Fällen tatsächlich kein XML-Dokument empfangen. Die Antwort war entweder eine Fehermeldung vom Varnish-Cache-Server, oder eine Weiterleitung aufgrund einer Regel in der .htaccess. Im ersten Fall konnte der Fehler durch Abschaltung des Varnish weiter analysiert werden. Im zweiten Fall wurde die Regel in der .htaccess verbessert.

WSDLSOAP-ERROR: Parsing Schema: can’t import schema from ‚http://schemas.xmlsoap.org/soap/encoding/‘

Fehlerbeschreibung

Beim Verbindungsaufbau mit Magento-API wird der nachfolgende Fehler gemeldet:

WSDLSOAP-ERROR: Parsing Schema: can't import schema from 'http://schemas.xmlsoap.org/soap/encoding/'

Mögliche Ursache

Der Aufruf der o. g. URL findet serverseitig statt. Vergewissern Sie sich, dass der Server die URL zugreifen kann:

wget http://schemas.xmlsoap.org/soap/encoding/

Wenn die Datei auf dem Server nicht heruntergeladen werden konnte, ist sie für den Server tatsächlich nicht erreichbar. Ausnahmsweise sagt die Fehlereldung tatsächlich etwas aus, was direkt mit der Ursache zu tun hat.

Lösung

Je nach Situation kann es verschiedene Lösungsansätze geben, warum der Server die URL nicht erreichen kann. In unserem Fallbeispiel lag es an einer Firewall-Einstellung.

CMS-Inhalte mit jQuery in eine statische Seite einfügen, CMS-Seite ohne html und body ausgeben

Bei einer stark besuchten Seite sollte eine Cache-Lösung wie Varnish eingesetzt werden. Der Nachteil einer gecachten Seite ist, dass ein Update eines Inhaltsblocks (z.B. einer Ankündigung oder einer Werbaktion), der auf allen Unterseiten angezeigt werden soll, durch den Cache nicht sofort möglich wäre.

Ohne der Erneuerung der gecachten Seiten kann der Inhalt des Blocks über jQuery geladen und in die gecachte Seite eingefügt werden. So entfällt das Cache-Warming, und der neue Inhalt ist sofort auf allen Seiten zu sehen.

Auch der dynamische Inhalt kann über eine CMS-Seite in Magento verwaltet werden. Um ihn über jQuery in die vorhandene Seitenstruktur sauber und effizient einfügen zu können, sollten die Inhalte der CMS-Seite in Roh-Form ausgegeben werden.

Roh-Ausgabe von CMS-Inhalten

Mit dem Layout „Empty“ und durch Entfernen des head-Blocks mit dem folgenden XML-Layout-Update können CMS-Inhalte unformatiert ausgegeben werden:

<remove name="head" />

Wenn wir die Quellecode-Ausgabe dieser CMS-Seite im Frontend ansehen, stellen wir fest, dass der Inhalt in ein nicht ganz leeres Layout eingebettet wird:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
    </head>
<body class="page-empty cms-page-view cms-hello">
    <div>
        <div class="std"><p>Hallo Welt</p></div></div>
</body>
</html>

Dies hängt damit zusammen, dass für diese Ausgabe die Vorlage unter app/design/frontend/base/default/template/page/empty.phtml verwendet wird, die in der Tat nicht leer ist:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="<?php echo $this->getLang() ?>" lang="<?php echo $this->getLang() ?>">
<head>
    <?php echo $this->getChildHtml('head') ?>
</head>
<body>
    <div>
        <?php echo $this->getChildHtml('after_body_start') ?>
        <?php echo $this->getChildHtml('global_messages') ?>
        <?php echo $this->getChildHtml('content') ?>
        <?php echo $this->getChildHtml('before_body_end') ?>
        <?php echo $this->getAbsoluteFooter() ?>
    </div>
</body>
</html>

Für die dynamische Einbindung der CMS-Inhalte per jQuery in eine gecachte Seite, sollten die CMS-Inhalte in ihrem Ursprungszustand ausgegeben werden, d. h. ohne html- und body-Tag. Dies ist mit dem eigenem wirklich leeren Template möglich:

In unserem Template-Verzeichnis (z. B. app/design/frontend/default/default/template/page) legen wir die Datei really-empty.phtml mit folgendem Inhalt an:

<?php echo $this->getChildHtml('content') ?>

In das Feld XML für Layoutänderungen tragen wir folgenden XML-Code ein:

<remove name="head" />

<reference name="root">
    <action method="setTemplate">
       <template>page/really-empty.phtml</template>
    </action>
</reference>

CMS-Inhalte mit jQuery einfügen

Nun bekommen wir tatsächlich nur <p>Hallo Welt</p> ausgegeben und könnten diese Inhalte dynamisch in eine statische Seite einfügen:

<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$.noConflict();
jQuery(document).ready(function() {
    jQuery('#dynamic-content').load('/magento1702sd/hello');
});
</script>
</head>
<body>

<div id="dynamic-content"></div>

</body>
</html>

Weiterlesen:

Automatische Warnung vor Verwendung geschützter Wortmarken

Hin und wieder bekommen unsere Kunden und auch wir bei unseren eigenen Projekten Abmahnungen wegen Verwendung geschützter Wortmarken. Manchmal sind geschützte Worte so alltäglich, dass der Shopbetreiber nicht mal vermuten würde, dass das Wort geschützt ist.

Wussten Sie z. B. dass der Satz „Berlin gefällt mir“ (s. DPMA-Eintrag) eine geschützte Wortmarke ist? Wenn Sie Ihre Liebe zur Hauptstadt so beispielsweise auf einem T-Shirt zum Ausdruck bringen und es zum Verkauf anbieten, werden Sie ziemlich schnell Post von den Anwälten des Markeninhabers bekommen und es wird richtig teuer. Es gibt überraschend viele Beispiele für Wortmarken dieser Art!

Eine Dienstleistung, die sämtliche Texte des Online-Shops (Produkt-Attribute, CMS-Seiten und statische Blöcke) auf gesicherte Wortmarken prüft und den Betreiber vorwarnt, haben wir nicht gefunden! Entsprechende Filter für Nizza-Klassen, die zum Thema des Shops passen, sollen die Wortmarken thematisch eingrenzen und so die Zahl irrelevanter Warnungen reduzieren.

Welche Marken bereits existieren, können Sie manuell über die DPMA-Einsteigerrecherche herausfinden:

Internationale Recherche ist u. a. mit WIPO möglich.

XAMPP/Apache startet nicht, Skype blockiert Port 80

Fehlerbeschreibung

XAMPP/Apache unter Windows startet nicht. Es wird die folgende Fehlermeldung angezeigt:

Error: Apache shutdown unexpectedly.
This may be due to a blocked port, missing dependencies,
improper privileges, a crash, or a shutdown by another method.
Check the "/xampp/apache/logs/error.log" file
and the Windows Event Viewer for more clues

Mögliche Ursache

Apache wurde bereits gestartet. In diesem Fall ist der Prodzess httpd.exe im Taskmanager auffindbar und kann darüber beendet werden.

Wenn Apache nicht läuft, wird Port 80 möglicherweise von Skype blockiert.

Mögliche Lösung

Im Skype-Menü unter Aktionen > Optionen > Erweitert > Verbindung deaktivieren Sie die Option Ports 80 und 443 für zusätzliche eingehende Verbindungen verwenden und starten Sie Skype neu.

Um Skype neu zu starten, muss Skype beendet werden (es reicht nicht Skype zu schließen). Klicken dazu mit der rechten Maustaste auf Skype in der Taskleiste und dann auf Beenden. Unter Windows 7 sieht das Kontextmenü wie folgt aus:

Weiterlesen:

Magento über das Meta-Modul Mage_All_Latest updaten

Wann haben Sie Magento das letzte Mal über den Download-Manager geupdatet?

Eine tägliche Beschäftigung ist das nicht und so wird gerne vergessen, dass beim Update des gesamten Systems über den Download-Manager nicht jedes einzelne Modul in seiner neuesten Version manuell ausgewählt werden muss, sondern nur das Meta-Paket Mage_All_Latest:

Nach dem Update von Mage_All_Latest werden alle Magento-Core-Module auf die neueste Version geupdatet.

 

Magento-SOAP-Anfragen loggen und debuggen

Bei der Arbeit mit der Magento-API bietet Magento von sich aus keine Debugging-Möglichkeiten. Wer herausfinden will, wie die XML-Anfrage ausschaut, die an Magento gesendet wurde, dem hilft die Erweiterung Inchoo_SoapLogger (GitHub) weiter!

Nach dem Kopieren der Erweiterung in das Magento-Verzeichnis und nach dem Aktualisieren des Magento-Konfigurations-Caches, wird der nächste API-Call in der Datei var/v1_soap.log bzw. var/v2_soap.log (im var-Verzeichnis von Magento) gespeichert. Die Erweiterung verwendet zum Schreiben der Dateien Mage::log, und erfordert, dass der Magento-Log in System > Konfiguration > Erweitert: Entwickleroptionen > Log Einstellungen aktiviert ist:

Wie die Erweiterung im Detail funktioniert, wird vom Entwickler hier dokumentiert.

Erweiterung von GitHub herunterladen.

Fehler bei API-Verbindung mit Magento: Uncaught SoapFault exception: [HTTP] Unable to parse URL in …

Fehlerbeschreibung

Ein PHP-Skript versucht eine API-Verbindung mit einem Magento-Shop herzustellen:

$client = new SoapClient('http://beispiel.de/index.php/api/v2_soap/?wsdl');

Der Versuch endet mit der folgenden Fehlermeldung:

Fatal error: Uncaught SoapFault exception: [HTTP] Unable to parse URL in ...script.php

Es kommt zu dem Fehler, obwohl die URL http://beispiel.de/index.php/api/v2_soap/?wsdl erreichbar ist und einen gültigen XML-Code zurückgibt.

Ursache

Suchen Sie in der XML-Ausgabe unter http://beispiel.de/index.php/api/v2_soap/?wsdl nach soap:address:

<service name="MagentoService">
    <port name="Mage_Api_Model_Server_V2_HandlerPort" binding="typens:Mage_Api_Model_Server_V2_HandlerBinding">
        <soap:address location="https://beispiel.de/index.php/api/v2_soap/index/"/>
    </port>
</service>

Der Wert des Attributs location von soap:address muss eine gültige URL enthalten:

  • Gültige URL:
    https://beispiel.de/index.php/api/v2_soap/index/
  • Ungültige URL, Fall 1: Abweichende Basis-URL:
    https://andere-domain.de/index.php/api/v2_soap/index/
  • Ungültige URL, Fall 2: Basis-URL fehlt:
    /api/v2_soap/index/

Lösung

Der Wert des Attributs location von soap:address muss eine absolute und gültige URL enthalten!

  • Der 1. Fall ist der häufigste, bei dem die URL unter der die API aufgerufen wurde, nicht mit der in der Systemkonfiguration eingestellten URL übereinstimmt (dies kommt typischerweise nach einem Domain-Umzug oder in einer Testumgebung vor, die unter einer anderen Domain erreichbar ist).

Überprüfen und ändern Sie ggf. die Einstellung unter System > Konfiguration > Allgemein: Web > Ungesichert/Gesichert > Basis-URL.

  • Der 2. Fall ist sehr speziell, da bei diesem Projekt die Basis-URL bei der Ausgabe im gesamten Shop-Frontend (aufgrund bestimmter Anforderungen) entfernt wird.

Durch Anpassung des Ausgabefilters, sodass die Basis-URL bei API-Ausgaben nicht entfernt wird, konnte der Fehler in diesem Fall behoben werden.

Weiterführende Links:

Magento-API: Bestellte Produkte mit parent_item_id abfragen

Bestellungen können in Magento über die API ausgelesen und von einer externen Software (z. B. Warenwirtschaft, Fakturierung) importiert werden.

Wenn in einer Bestellung jedoch zusammengesetzte Produkte wie Bündelartikel (bundle product) enthalten sind, wird die Spalte parent_item_id bei der API-Abfrage der Bestellposten standardmäßig nicht übermittelt. Diese Spalte ist aber entscheidend, um einzelne Teilprodukte dem Hauptprodukt zuordnen zu können!

Dies hängt damit zusammen, dass jedes Attribut für die Ausgabe explizit in der WSDL-Definitionsdatei angegeben werden muss. In der mitgelieferten WSDL-Definition für Bestellposten wurde die Spalte parent_item_id offenbar vergessen. In der aktuellen Magento-Version 1.9.0.1 (vom 16.05.2014) ist das immer noch der Fall.

Durch eine Anpassung der WSDL-Definition, kann die Spalte parent_item_id (und weitere Spalten, die in der SQL-Tabelle sales_flat_order_item zu finden sind) nachträglich eingefügt und über die API ausgegeben werden.

Dazu wird die Datei app/code/core/Mage/Sales/etc/wsdl.xml direkt im core-Verzeichnis angepasst! Die angepasste Datei app/code/local/Mage/Sales/etc/wsdl.xml im local-Code-Pool wird von Magento (nach Löschung des gesamten Magento-Caches) leider ignoriert. Wenn es eine elegantere Methode für diese Anpassung gibt, freuen wir uns über Ihre Kommentare.

Die fehlende Spalte parent_item_id wird ähnlich wie andere Spalten (z. B. item_id) hinzugefügt.

<complexType name="salesOrderItemEntity">
    <all>
        ...
        <element name="parent_item_id" type="xsd:string" minOccurs="0" />
        ...

Der guten Ordnung halber haben wir die Spalte parent_item_id genau dort eingefügt, wo sie in der ursprünglichen Tabelle sales_flat_order_item zu finden ist, zwischen order_id und quote_item_id:

<complexType name="salesOrderItemEntity">
    <all>
        <element name="item_id" type="xsd:string" minOccurs="0" />
        <element name="order_id" type="xsd:string" minOccurs="0" />
        <element name="parent_item_id" type="xsd:string" minOccurs="0" />
        <element name="quote_item_id" type="xsd:string" minOccurs="0" />
        <element name="created_at" type="xsd:string" minOccurs="0" />
        <element name="updated_at" type="xsd:string" minOccurs="0" />
        <element name="product_id" type="xsd:string" minOccurs="0" />
        <element name="product_type" type="xsd:string" minOccurs="0" />
        <element name="product_options" type="xsd:string" minOccurs="0" />
        <element name="weight" type="xsd:string" minOccurs="0" />
        <element name="is_virtual" type="xsd:string" minOccurs="0" />
        <element name="sku" type="xsd:string" minOccurs="0" />
        <element name="name" type="xsd:string" minOccurs="0" />
        <element name="applied_rule_ids" type="xsd:string" minOccurs="0" />
        <element name="free_shipping" type="xsd:string" minOccurs="0" />
        <element name="is_qty_decimal" type="xsd:string" minOccurs="0" />
        <element name="no_discount" type="xsd:string" minOccurs="0" />
        <element name="qty_canceled" type="xsd:string" minOccurs="0" />
        <element name="qty_invoiced" type="xsd:string" minOccurs="0" />
        <element name="qty_ordered" type="xsd:string" minOccurs="0" />
        <element name="qty_refunded" type="xsd:string" minOccurs="0" />
        <element name="qty_shipped" type="xsd:string" minOccurs="0" />
        <element name="cost" type="xsd:string" minOccurs="0" />
        <element name="price" type="xsd:string" minOccurs="0" />
        <element name="base_price" type="xsd:string" minOccurs="0" />
        <element name="original_price" type="xsd:string" minOccurs="0" />
        <element name="base_original_price" type="xsd:string" minOccurs="0" />
        <element name="tax_percent" type="xsd:string" minOccurs="0" />
        <element name="tax_amount" type="xsd:string" minOccurs="0" />
        <element name="base_tax_amount" type="xsd:string" minOccurs="0" />
        <element name="tax_invoiced" type="xsd:string" minOccurs="0" />
        <element name="base_tax_invoiced" type="xsd:string" minOccurs="0" />
        <element name="discount_percent" type="xsd:string" minOccurs="0" />
        <element name="discount_amount" type="xsd:string" minOccurs="0" />
        <element name="base_discount_amount" type="xsd:string" minOccurs="0" />
        <element name="discount_invoiced" type="xsd:string" minOccurs="0" />
        <element name="base_discount_invoiced" type="xsd:string" minOccurs="0" />
        <element name="amount_refunded" type="xsd:string" minOccurs="0" />
        <element name="base_amount_refunded" type="xsd:string" minOccurs="0" />
        <element name="row_total" type="xsd:string" minOccurs="0" />
        <element name="base_row_total" type="xsd:string" minOccurs="0" />
        <element name="row_invoiced" type="xsd:string" minOccurs="0" />
        <element name="base_row_invoiced" type="xsd:string" minOccurs="0" />
        <element name="row_weight" type="xsd:string" minOccurs="0" />
        <element name="gift_message_id" type="xsd:string" minOccurs="0" />
        <element name="gift_message" type="xsd:string" minOccurs="0" />
        <element name="gift_message_available" type="xsd:string" minOccurs="0" />
        <element name="base_tax_before_discount" type="xsd:string" minOccurs="0" />
        <element name="tax_before_discount" type="xsd:string" minOccurs="0" />
        <element name="weee_tax_applied" type="xsd:string" minOccurs="0" />
        <element name="weee_tax_applied_amount" type="xsd:string" minOccurs="0" />
        <element name="weee_tax_applied_row_amount" type="xsd:string" minOccurs="0" />
        <element name="base_weee_tax_applied_amount" type="xsd:string" minOccurs="0" />
        <element name="base_weee_tax_applied_row_amount" type="xsd:string" minOccurs="0" />
        <element name="weee_tax_disposition" type="xsd:string" minOccurs="0" />
        <element name="weee_tax_row_disposition" type="xsd:string" minOccurs="0" />
        <element name="base_weee_tax_disposition" type="xsd:string" minOccurs="0" />
        <element name="base_weee_tax_row_disposition" type="xsd:string" minOccurs="0" />
    </all>
</complexType>

Nach dieser Anpassung muss neben dem Magento-Cache „Konfiguration“ in System > Cache-Verwaltung, noch der WSDL-Cache geleert werden, sofern dieser aktiviert war. Die Einstellung dazu finden Sie in System > Konfiguration > Services: Magento Core API:

Für bessere Performance der API-Abfragen empfehlen wir den WSDL-Cache zu aktivieren bzw. aktiviert zu lassen. Der WSDL-Cache wird durch Deaktivierung im Magento-Backend nicht gelöscht! Dazu müssen die Dateien im Systemverzeichnis des WSDL-Cache über die Kommandozeile entfernt werden. Je nach Serverkonfiguration, kann der WSDL-Cache sich in einem anderen Verzeichnis befinden, das über phpinfo() herausgefunden werden kann:

In unserer Serverumgebung befindet sich der WSDL-Cache in /tmp (STRG+F nach soap.wsdl_cache_dir) und kann wie folgt geleert werden:

cd /tmp
rm -rf *

Mit dem folgenden Beispielcode kann ein PHP-Client einzelne Produkte einer Bestellung ausgeben:

<pre><?php
$client = new SoapClient('http://beispiel.de/index.php/api/v2_soap/?wsdl');
$session = $client->login('api-user', 'geheim123');

$result = $client->salesOrderInfo($session, '1000000001');
$items = $result->items;
foreach($items as $item) {
    print 'item_id:'.$item->item_id."\n";
    print 'product_type:'.$item->product_type."\n";
    if(isset($item->parent_item_id)) print 'parent_item_id:'.$item->parent_item_id."\n";
    print 'name:'.$item->name."\n";
    print "--------------------------\n";
}

In der Beispielausgabe ist das erste Produkte mit der item_id=10 ein Bündelprodukt, welches such aus den unterstehenden einfachen Produkten zusammensetzt, was über die parent_item_id=10 nachvollziehbar ist:

item_id:10
product_type:bundle
name:Beispiel-Bündelprodukt
--------------------------
item_id:5
product_type:simple
parent_item_id:10
name:Einfaches-Produkt-1
--------------------------
item_id:12
product_type:simple
parent_item_id:10
name:Einfaches-Produkt-2
--------------------------
item_id:7
product_type:simple
parent_item_id:10
name:Einfaches-Produkt-3

Varnish-Neustart: Running VCC-compiler failed, exit 1 – VCL compilation failed

Fehlerbeschreibung

Scheinbar nach einer Anpassung der VCL-Datei startet Varnish nicht und meldet einen Fehler ohne genauer Beschreibung oder Zeilenreferenz:

root@test-01:~# service varnish restart
 * Stopping HTTP accelerator varnishd              [fail]
 * Starting HTTP accelerator varnishd              [fail]
Running VCC-compiler failed, exit 1

VCL compilation failed

Der Wechsel der VCL-Datei auf eine frühere Version mit der Varnish auf der gleichen Maschine bereits funktioniert hatte, behebt das Problem nicht.

Mögliche Ursache

Auf der genutzten Partition steht nicht genug Speicherplatz zur Verfügung.

Lösung

apt-get remove varnish brachte in diesem Fall den entscheidenden Hinweis:

Reading package lists... Done
Building dependency tree
Reading state information... Done
The following packages will be REMOVED:
  varnish
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
After this operation, 1,288 kB disk space will be freed.
Do you want to continue [Y/n]? Y
cut: write error: No space left on device
E: Problem executing scripts DPkg::Pre-Invoke 'if [ -x /usr/bin/etckeeper ]; then etckeeper pre-install; fi'
E: Sub-process returned an error code

cut: write error: No space left on device, d. h. der Speicher ist voll. Der folgende Befehl liefert eine Übersicht aller Partitionen und Speicherverbrauch:

df -h

Nach Freigabe des Speichers auf der Partition, die in das Verzeichnis var gemountet war, konnte Varnish fehlerfrei neugestartet werden.

Call to a member function getMetaTitle() on a non-object

Fehlermeldung

Fatal error: Call to a member function getMetaTitle() on a non-object
in app/code/core/Mage/Catalog/Block/Category/View.php on line 44

Ursache

Der Fehler tritt beim Zugriff auf den Metatitel der aktuellen Kategorie in Mage_Catalog_Block_Category_View:

$category = $this->getCurrentCategory();
if ($title = $category->getMetaTitle()) {
   $headBlock->setTitle($title);
}

Ist das Produkt der Website zugeordnet aber keiner Kategorie darin, kommt es zu diesem Fehler, sofern im Template auf die eigenschaften der aktuellen Kategorie zugegriffen wird.

Lösung

Wenn der Artikel absichtlich in keiner Kategorie gelistet, aber direkt aufgerufen können werden soll, kann der Artikel der Wurzelkategorie der zugehörigen Website zugeordnet werden.

Memcached und Magento: Can’t get filling percentage

Beispielfehlermeldung

Can't get filling percentage

Ursache

Memcached läuft nicht oder wurde neugestartet.

Fehlerbehebung

Memecached und danach Apache neu starten:

sudo service memcached restart
sudo service apache2 restart

Konfiguration und Ressourcen überprüfen.

Marktanteil von Magento, Update 2014

Im Juni 2012 haben wir das erste Mal den Marktanteil von Magento dokumentiert.

Marktanteile nach Wappalyzer

Nach Wappalyzer ist der prozentuelle Anteil der Magento-Shops auf 22% gesunken, bei absoluten Werten bleibt Magento aber weiterhin mit Abstand die ungeschlagene Nummer eins. Top 10 E-Commerce-Anwendungen nach Wappalyzer (Momentaufnahme vom 30.05.2014):

Platz Anwendung Websites Besuche
1 Magento 113.384 14.422.458
2 OpenCart 7.488 5.140.300
3 Prestashop 74.256 4.541.016
4 WooCommerce 6.318 5.886.903
5 osCommerce 39.522 1.562.696
6 Shopify 25.253 1.240.091
7 VirtueMart 20.467 570.695
8 Bigcommerce 19.839 939.041
9 Zen Cart 11.283 39.931
10 eZ Publish 8.615 970.309

Auch interessant ist die Verteilung der Sprachen, in den die Shop-Systeme betrieben werden. Die Spracherkennung von Wappalyzer scheint aber nicht immer zuverlässig zu sein. Für WooCommerce fehlt das Diagramm völlig, für OpenCart erscheint die Statistik nicht plausibel:

Google Trends in Deutschland und weltweit

Diese Popularität von Magento wird von Google Trends bestätigt:

Seit einiger Zeit feiert sich Magento Connect als „… the largest eCommerce application marketplace in the world“.

SISTRIX-Sichtbarkeit in Google.de und Google.com

Die nachfolgenden Charts zeigen den zeitlichen Verlauf der SISTRIX-Sichtbarkeit in Google.de und Google.com für die Hauptseiten der Top 3 E-Commerce-Anwendungen Magento, OpenCart und Prestashop.

Vereinfacht beschrieben, errechnet sich die SISTRIX-Sichtbarkeit aus der Anzahl der Platzierungen in den Top 100 für ein Katalog von 250.000+ Suchbegriffen gewichtet mit der Popularität des jeweiligen Suchworts. In die Berechnunng fließen noch weitere Faktoren ein wie die Platzierung an sich. Aus der Sichtbarkeit kann u. a. die relative Anzahl der Besucher geschätzt werden.

Durch Umstrukturierung der Webpräsenz und teilweisen Umzug der Inhalte von magentocommerce.com auf magento.com hat die Magento-Hauptseite Platzierungen und damit Sichtbarkeit bei Google.de verloren:

Bei Google.com bleibt Magento hinsichtlich der Sichtbarkeit auch heute noch klarer Sieger (die Sichtbarkeit der Seiten in Google.com wird erst seit 2011 aufgezeichnet):

Weiterlesen: Magento-Marktanteile in 2012 und 2013.

Schnelle Suche in System-Einstellungen: Admin Config Quick Search

Admin Config Quick Search auf Magento Connect
Mit der Erweiterung Admin Config Quick Search (GitHub: Magento Quick Config Module) können die Systemeinstellungen von Magento schnell durchsucht werden.

Dies kann einem Magento-Einsteiger, der noch nicht alle Systemeinstellungen kennt, sehr viel Zeit sparen. Aber auch einem Dienstleister, der mehrere Magento-Shops verwaltet, die jeweils eine individuelle Modifikation und Zusammenstellung von Magento-Erweiterungen mit speziellen Systemeinstellungen aufweisen, stellt diese Suchfunktion einen Mehrwert dar.

Nach der Einrichtung der Erweiterung taucht in den Systemeinstellungen oben rechts ein Suchfeld auf. Die Suche wird während der Eingabe ausgeführt:

Download der Erweiterung:

Weiterlesen: Erweiterungen von Magento Connect herunterladen.

Magento-Erweiterungen herunterladen und analysieren

Magento Connect bietet leider keine Möglichkeit eine gepackte Erweiterung direkt herunterzuladen, um sie vor der Installation in den Shop analysieren zu können.

Eine Möglichkeit wäre die Erweiterung über freegento.com herunterzuladen. Die andere Möglichkeit bietet Magento selbst: Die Erweiterung kann über den Download-Manager in Ihrer Magento-Testumgebung heruntergeladen und installiert werden. Die gepackte tgz-Datei ist danach im Magento-Verzeichnis unter downloader/.cache/community zu finden!

Unsere Empfehlung für eine sichere Installation von Magento-Erweiterungen:

  1. Reviews und Kommentare auf Magento Connect lesen
  2. Veröffentlichungsdatum der letzten Version der Erweiterung nachschlagen
  3. Automatisch ermittelte Qualität der Erweiterung bei Judge nachschlagen
  4. Die Erweiterung herunterladen (s. o.) und den Code von einem Magento-Entwickler überfliegen lassen, insbesondere nach curl-Aufrufen und externen Ressourcen (darunter externe Logo-Grafiken) Ausschau halten!
  5. In einer Magento-Entwicklungsumgebung installieren und vollständig testen
  6. Produktivumgebung sichern
  7. In die Produktivumgebung übertragen
  8. Cache-Aktualisierung
  9. Vollständiger Test der Produktivumgebung

Shop während der Entwicklung vor Google-Indizierung und unerwünscheten Besuchern schützen

Mit dem HTTP-Passwort lässt sich der Shop in der Aufbauphase gut vor unerwünschten Besuchern und Indizierung durch Suchmaschinen schützen.

Der etwas umständlichere Weg ist die Einrichtung des HTTP-Passwors über die Server-Konfigurationsdateien .htaccess und .htusers (mehr Details dazu bei SELFHTML).

Da in Magento alle relevanten Anfragen über die index.php abgearbeitet werden (Single Point of Entry), kann der Passwort-Schutz direkt dort umgesetzt werden. Dazu kann am Anfang der index.php nach dem <?php der folgende Code eingebaut werden:

<?php

define('ADMIN_USERNAME','user');
define('ADMIN_PASSWORD','geheim123');

if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) ||
        $_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME ||$_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) {

    Header("WWW-Authenticate: Basic realm=\"Magento Development Environment\"");
    Header("HTTP/1.0 401 Unauthorized");
    echo 'Access Denied. ' . time();
    exit;
}

Damit werden Frontend und Backend geschützt. Doch im Backend kann der Flash-Uploader für Produktbilder mit dem Passwortschutz nicht umgehen und meldet den Fehler „Upload HTTP Error“:

Da der Zugang zum Backend bereits geschützt ist, kann er durch eine entsprechende Erweiterung des Codes vom HTTP-Passwortschutz ausgeschlossen werden:

define('ADMIN_URL','admin');
define('ADMIN_USERNAME','user');
define('ADMIN_PASSWORD','geheim123');

if(strpos($_SERVER['REQUEST_URI'], ADMIN_URL) === false) {
    if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) ||
               $_SERVER['PHP_AUTH_USER'] != ADMIN_USERNAME ||$_SERVER['PHP_AUTH_PW'] != ADMIN_PASSWORD) {

        Header("WWW-Authenticate: Basic realm=\"Magento Development Environment\"");
        Header("HTTP/1.0 401 Unauthorized");
        echo 'Access Denied. ' . time();
        exit;
    }
}

SEPA-Lastschrift mit DebitPayment

Mit der kostenfreien Erweiterung DebitPayment ist die grundlegende Abwicklung der Zahlung per Lastschrift möglich.

In System > Konfiguration > Verkäufe: Zahlarten > Bankeinzug bietet die Erweiterungen eine reihe praktischer Einstellmöglichkeiten, darunter Anzeige abhängig von der Kundengruppe und Mindest- und Höchstbestellwerte:

Nach der Aktivierung und Umstellung der Art auf SEPA, wird im Checkout die Eingabe der IBAN und BIC möglich:

Die Prüfung der Eingabe findet leider nicht statt. Die Eingabefelder sind zwar als Pflichtfelder eingestellt, doch bereits Eingabe von einem Zeichen in jedes Feld wird akzeptiert:

Die Bankverbindung wird in den Bestelldetails gespeichert. In diesem Fall wurde in jedes Feld ein „.“ eingegeben:

Um die Export-Funktionalität zu nutzen, muss unter System > Konfiguration > Verkäufe: Zahlarten > Bankeinzug / Lastschrift die Bankverbindung des Shop-Betreibers eingetragen zu werden:

Danach können SEPA-Lastschrift-Daten in Verkäufe > Lastschrift-Bestellungen im CSV-Format wie folgt exportiert werden:

  1. Bestellungen abgleichen
  2. Nicht exportierte Bestellungen (Status-Spalte) filtern und wählen
  3. CSV-Export

Die Erteilung des SEPA-Mandats müsste der Shop-Betreiber manuell durchführen.

Weiterlesen:

Verteilung der E-Mail-Anbieter unter Newsletter-Abonnenten und Kunden

Beim Versand von Newslettern ist es praktisch zu wissen, welche Email-Anbieter die Kunden verwenden. Die folgende Abfrage extrahiert die Domain des Anbieters und zählt die Email-Adressen der Newsletter-Abonnenten:

SELECT provider_domain AS provider, count(subscriber_email) AS number FROM (
    SELECT
    subscriber_email,
    SUBSTRING_INDEX(subscriber_email, '@', -1) provider_full,
    SUBSTRING_INDEX(SUBSTRING_INDEX(subscriber_email, '@', -1), '.', -2) provider_domain
    FROM newsletter_subscriber
) AS t
GROUP BY provider
ORDER BY number DESC;

Leicht angepasst, kann diese Abfrage auf alle Kunden angewendet werden:

SELECT provider_domain AS provider, count(email) AS number FROM (
    SELECT
    email,
    SUBSTRING_INDEX(email, '@', -1) provider_full,
    SUBSTRING_INDEX(SUBSTRING_INDEX(email, '@', -1), '.', -2) provider_domain
    FROM customer_entity
) AS t
GROUP BY provider
ORDER BY number DESC;

Weiter optimiert, fasst die Anfrage Anbieter mit weniger als 50 Email-Adressen in eine Zeile mit dem Eintrag „OTHER“ zusammen:

SELECT provider, SUM(number) number FROM (
SELECT IF(number < 50, '* OTHER', provider) provider, number FROM (
  SELECT provider_domain provider, GROUP_CONCAT(DISTINCT provider_full) domains, COUNT(email) AS number FROM (
    SELECT
    email,
    SUBSTRING_INDEX(email, '@', -1) provider_full,
    SUBSTRING_INDEX(SUBSTRING_INDEX(email, '@', -1), '.', -2) provider_domain
    FROM customer_entity
  ) AS t
  GROUP BY provider_domain
) AS v
) AS w
GROUP BY provider
ORDER BY number DESC;

Einige Anbieter-Domains (z.B. gmail und googlemail) sollten noch manuell zusammengefasst werden.

Um auch noch die Summe anzeigen zu lassen, kann WITH ROLLUP verwendet werden. Um die Liste absteigend sortiert zu lassen, ist allerdings eine weitere Schachtelung nötig:

SELECT IF(provider IS NULL, '* TOTAL', provider) provider, number FROM (
SELECT provider, SUM(number) number FROM (
SELECT IF(number < 50, '* OTHER', provider) provider, number FROM (
  SELECT provider_domain provider, GROUP_CONCAT(DISTINCT provider_full) domains, COUNT(email) AS number FROM (
    SELECT
    email,
    SUBSTRING_INDEX(email, '@', -1) provider_full,
    SUBSTRING_INDEX(SUBSTRING_INDEX(email, '@', -1), '.', -2) provider_domain
    FROM customer_entity
  ) AS t
  GROUP BY provider_domain
) AS v
) AS w
GROUP BY provider
WITH ROLLUP
) AS x
ORDER BY number DESC

Varnish und Google Analytics

Links mit Analytics-Kamagnen-Parametern werden in der Standard-Konfiguration von Varnish nicht als Seiten mit identischem Inhalt erkannt. Die beiden nachfolgenden URL werden von Varnish als verschiedene Seiten erkannt und deshalb unabhängig voneinander gecacht:

  1. URL: http://xonu.de
  2. URL: http://xonu.de?utm_campaign=ny2014&utm_medium=banner&utm_source=extern

Während die erste URL von einem Cache-Warmer vorbereitet wird, ist die zweite URL dem Cache-Warmer typischerweise nicht bekannt, sodass der erste Besucher etwas länger auf die Inhalte warten muss, obwohl sie aus dem Cache hätten geliefert werden können.

Damit dies geschieht, muss Varnish mitgeteilt werden, dass Analytics-Parameter den Inhalt nicht verändern. Varnish berechnet einen Hash basierend auf der angefragten URL. Diese URL kann in vcl_recv vor der Berechnung des Hashs manipuliert werden.

Durch Entfernen aller Analytics-Parameter aus der URL wird dem Varnish mitgeteilt, dass sie die Inhalte nicht verändert, sodass für die erste und zweite URL gleiche Inhalte ausgeliefert werden können.

In vcl_recv wird der folgende Zusatzblock hinzugefügt:

sub vcl_recv {

  if (req.url ~ "(\?|&)(utm_[^=]+)=") {
    set req.url = regsub(req.url, "\\\?", "?");
    set req.url = regsuball(req.url, "&(utm_[^=]+)=([^&]+)", "");
    set req.url = regsuball(req.url, "\?(utm_[^=]+)=([^&]+)", "?");
    set req.url = regsub(req.url, "\?&", "?");
    set req.url = regsub(req.url, "\?$", "");
  }

  ....

}

Alle Analytics-Parameter (s. Analytics URL Builder) starten mit „utm_“:

  • utm_source: Kampagnenquelle
  • utm_medium: Kampagnenmedium
  • utm_term: Kampagnenbegriff
  • utm_content: Kampagnen-Content
  • utm_campaign: Kampagnenname

Wird also ein solcher Parameter erkannt, wird die URL davon bereinigt:

  • Zeile 4 ersetzt /? durch ? (ohne Slash).
  • Zeile 5 entfernt Analytics-Parameter, die nicht an erster Stelle stehen. In URL 2 werden hierdurch &utm_medium=banner und &utm_source=extern entfernt.
  • Zeile 6 entfernt Analytics-Parameter, die an erster Stelle stehen. In URL 2 wird dadurch ?utm_campaign=ny2014 entfernt.
  • Zeile 7 entfernt das überflüssige &-Zeichen, wenn die URL neben Analytics nicht weitere GET-Parameter an nicht-erster Stelle enthalten hatte (aus ?utm_campaign=ny2014&p=2 wäre ohne dieser Zeile ?&p=2 geworden).
  • In dem Fall, dass alle GET-Parameter in der URL Analytics-Parameter waren, entfernt die Zeile 8 das übrig gebliebene ?-Zeichen.

Da die Auswertung der Kampagnen-Parameter durch den Analytics-Tracker Client-seitig durch JavaScripte geschieht, bleibt das Tracking davon unbetroffen.

Update auf 1.8.1 und Kunden können sich nicht mehr einloggen, Magento-Frontend-Login funktioniert nicht mehr

Berichte häufen sich, dass das Frontend-Login nach Update auf Magento 1.8.1 nicht mehr funktioniert, d. h. nach Eingabe der richtigen Login-Daten wird ohne Fehlermeldungen erneut das Login-Formular angezeigt, während bei Eingabe falscher Benutzerdaten eine entsprechende Fehlermeldung angezeigt wird.

In einem Shop konnten wir den Fehler durch Hinzufügen des folgenden hidden-Felds in das Login-Formular beheben:

<input type="hidden" name="form_key" value="<?php echo Mage::getSingleton('core/session')->getFormKey(); ?>" />

Pfad der zu bearbeitenden Template-Datei:

app/design/frontend/base/default/template/persistent/customer/form/login.phtml

Der Code kann direkt in der nächsten Zeile nach nach <form …> eingefügt werden. Vor dem Neuladen des Formulars im eigenen Browser kann das Löschen von Cookies hilfreich sein.

Über FireBug können dabei gezielt die Cookies der Shop-Domain gelöscht werden:

Texthinweise im Magento-Checkout: Statische Blöcke oberhalb und unterhalb der Zusammenfassung einbauen

Diese Anleitung zeigt, wie man eigene Texthinweise im letzten Checkout-Schritt anzeigen lassen kann. Die Inhalte werden über statische Blöcke verwaltet, die dann über ein Layout-Update eingebunden werden.

Wir gehen davon aus, dass im Shop German Setup verwendet wird, es ist aber keine Voraussetzung. Wir wollen oberhalb der Zusammenfassung „Bei Zahlungseingang vor 15:00 Uhr erfolgt die Lieferung noch am gleichen Tag.“ und unterhalb „Bei Lieferungen in das Nicht-EU-Ausland fallen zusätzliche Zölle, Steuern und Gebühren an.“ anzeigen. Der zweite Hinweis ist für das Bestehen der TrustedShops-Zertifizierung erforderlich.

Der nachfolgende Screenshot demonstriert das Ergebnis:

Texte als statische Blöcke

Statische Blöcke lassen sich im Backend bequem verwalten. Deshalb sollen die beiden Hinweise als statische Blöcke angelegt werden. Wir legen zwei statische Blöcke mit den folgenden Bezeichnern an (die Namen wurden frei gewählt):

  • checkout_review_before
  • checkout_review_after

Der Inhalt des Blocks checkout_review_before ist:

<div style="border-color: #D9DDE3; border-style: solid; border-width: 0 1px; padding: 7px;">
Bei Zahlungseingang vor 15:00 Uhr erfolgt die Lieferung noch am gleichen Tag.
</div>

Der Inline-Style wurde aus der CSS-Regel-Definition für .opc .checkout-agreements aus styles.css übernommen, der CSS-Datei des Magento-Base-Themes. Die Gestaltung sorgt für die Fortsetzung des hellgrauen Rahmens und des Abstands des Texts vom Rand.

Der Inhalt des Blocks checkout_review_after ist:

<div style="border-width: 0 2px; border-style: none solid; padding-left: 10px;">
Bei Lieferungen in das Nicht-EU-Ausland fallen zus&auml;tzliche Z&ouml;lle, Steuern und Geb&uuml;hren an.
</div>

Auch hier sorgt der Inline-Style für die Fortsetzung des schwarzen Rahmens, der nach der Button-Lösung vorgeschrieben und für das Bestehen der TrustedShops-Zertifizierung erforderlich ist.

Der schwarze Rahmen wird von German Setup in skin/frontend/base/default/css/germansetup/checkout.css gestaltet. Der Inline-Style wurde von dort übernommen.

Statische Blöcke über Layout-Update im Checkout anzeigen

Über ein Layout-Update, ganz ohne Template-Anpassung, lassen sich statische Blöcke in Checkout-Schritte integrieren. Die vorher angelegte statische Blöcke können mit dem folgenden XML-Layout-Code oberhalb und unterhalb des letzten Checkout-Schritts eingebaut werden:

<?xml version="1.0"?>
<layout version="0.1.0">

    <checkout_onepage_review>
        <reference name="checkout.onepage.review.info.items.before">
            <block type="cms/block" name="checkout_review_before_block" before="-">
                <action method="setBlockId">
                    <block_id>checkout_review_before</block_id>
                </action>
            </block>
        </reference>

        <reference name="checkout.onepage.review.info.items.after">
            <block type="cms/block" name="checkout_review_after_block" after="-">
                <action method="setBlockId">
                    <block_id>checkout_review_after</block_id>
                </action>
            </block>
        </reference>
    </checkout_onepage_review>

</layout>

Der XML-Layout-Code wird in der Datei local.xml in app/design/frontend/default/default/layout/ oder im entsprechenden Theme-Verzeichnis gespeichert. Legen Sie die Datei an, falls sie noch nicht existiert.

Der Layout-Handle checkout_onepage_review und die beiden referenzierten Blöcke checkout.onepage.review.info.items.before und checkout.onepage.review.info.items.after werden in app/design/frontend/base/default/layout/checkout.xml erstmalig definiert und von German Setup in germansetup.xml (gleiches Verzeichnis) noch weiter modifiziert. Diese Anleitung wird also auch ohne installierten German Setup funktionieren.

Die beiden statischen Blöcke werden über XML-Layout-Code definiert. Da die Blöcke checkout.onepage.review.info.items.before und checkout.onepage.review.info.items.after als Strukturblöcke definiert wurden (type=core/text_list), werden die Inhalte aller untergeordneten Blöcke, also auch der statischen Blöcke, ausgegeben.

Nach dem Update des Layout-Cache in System > Cache Verwaltung sind die beiden Blöcke im Checkout sichtbar.

 

Weiterlesen:

Google Sitemaps und internationale Magento-Shops mit mehreren Domains

Google Webmaster Tools helfen die Indizierung eines Shops zu überwachen und zu kontrollieren. Wenn Sie einen internationalen Magento-Shop betreiben/einrichten, der unter verschiedenen Top-Level-Domains erreichbar sein soll, müssen einige Details bei der Verwaltung von Sitemaps beachtet werden.

Die sitemap.xml ist dabei sehr hilfreich und kann von Magento automatisch erstellt werden: Katalog > Google Sitemap.

Auch wenn Magento bei der Sitemap-Erstellung ein Feld für den Dateinamen anbietet, muss die XML-Datei unbedingt sitemap.xml heißen. Bei Verwendung eines anderen Namens, z. B. sitemap_de.xml, bekommt man eine seltsame Fehlermeldung:

Pfad „…“ ist nicht verfügbar und kann nicht verwendet werden.

Englische Version:

Path „…“ is not available and cannot be used.

Die Sitemaps für die verschiedenen Shops der gleichen Magento-Installation können in verschiedenen Unterverzeichnissen des Shop-Verzeichnisses erstellt werden:

  • sitemap/en/sitemap.xml
  • sitemap/de/sitemap.xml

Die Sitemaps sollen in die robots.txt eingetragen werden:

Sitemap: /sitemap/sitemap.xml

Da unter verschiedenen Shop-Domains die gleiche Datei robots.txt vorliegt, können mehrere Sitemaps in der robots.txt eingetragen werden. Laut sitemaps.org: Sitemaps & Cross Submits muss in diesem Fall der vollständige Domainname eingetragen werden:

Sitemap: http://www.mein-shop.de/sitemap/de/sitemap.xml
Sitemap: http://www.mein-shop.de/sitemap/en/sitemap.xml

Dabei ist es unerheblich, welche der Shop-Domains hier verwendet wird; Auch diese Variante wäre zulässig:

Sitemap: http://www.mein-shop.de/sitemap/de/sitemap.xml
Sitemap: http://www.mein-shop.com/sitemap/en/sitemap.xml

Wenn Sie unter der jeweiligen Shop-Domain jeweils die passende Sitemap unter der gleichen URL verwenden wollen, lässt sich dies über eine .htaccess-Weiche realisieren:

sitemap/.htaccess

RewriteEngine on

RewriteCond %{HTTP_HOST} ^(www.)?mein-shop.de$
RewriteRule sitemap.xml de/sitemap.xml [L]

RewriteCond %{HTTP_HOST} ^(www.)?mein-shop.com$
RewriteRule sitemap.xml en/sitemap.xml [L]

RewriteRule .* / [L]

robots.txt

Sitemap: /sitemap/sitemap.xml

Die beiden Sitemaps befinden sich weiterhin in den Unterverzeichnissen en und de:

  • sitemap/en/sitemap.xml
  • sitemap/de/sitemap.xml
  • sitemap/.htaccess

Mit der .htaccess-Weiche können wir die Sitemap unter http://www.mein-shop.de/sitemap/sitemap.xml und unter http://www.mein-shop.com/sitemap/sitemap.xml (ohne Angabe der Unterverzeichnisse en bzw. de) aufrufen und bekommen die sitemap.xml jeweils aus dem passenden Unterverzeichnis ausgegeben.

Damit die Sitemap regelmäßig automatisch erneuert wird, kann dazu ein Magento-interner Cronjob konfiguriert werden:
System > Konfiguration > Katalog: Google Sitemap > Einstellungen für die Erstellung

Die Voraussetzung, dass die Sitemap dann tatsächlich automatisch erstellt wird, ist der regelmäßige Aufruf der cron.php bzw. cron.sh im Hauptverzeichnis von Magento durch Crontab.

Um Ihren Shop vor DDoS-Attaken durch häufigen Aufruf von cron.php zu Schützen, sollten die Zugriffsrechte der cron.php so konfiguriert sein, dass sie nicht im Browser aufgerufen werden kann. Alternativ kann sie z.B. in cron123.php umbenannt werden (der Inhalt von cron.sh muss angepasst werden).

Gerne beraten und unterstützen wir Sie bei der Suchmaschinenoptimierung Ihres Online-Shops.

JavaScript und CSS über das Layout-XML-Code hinzufügen

Layout XML kann im Backend von Magento in Kategorien, Produkten und CMS-Seiten verwendet werden. XML-Code wird jeweils in das Feld Custom Layout Update im Register Design eingegeben. Das eingegebene Layout-XML-Code gilt dann für das bearbeitete Produkt, die Kategorie oder die CMS-Seite.

Zum Bearbeiten des globalen Layouts sollte in Magento die eigene local.xml im Layout-Verzeichnis app/design/frontend/default/mytheme des aktuellen Themes mytheme angelegt und bearbeitet werden. Alle Befehle müssen sich innerhalb des Knotens layout befinden:

<?xml version="1.0"?>
<layout version="0.1.0">

    ...

</layout>

Im Backend der der Layout-XML-Code ohne diesen Knoten verwendet.

In app/design/frontend/base/default/layout/page.xml ist der Block mit dem Namen head definiert und gibt die Inhalte des HTML-Bereichs <head> aus. Mit der Block-Methode addItem() kann diesem Bereich ein JavaScript oder CSS hinzugefügt werden. Die Methode erwartet zwei Parameter: Pfad-Typ und den Dateinamen relativ zum Pfad-Typ.

Vordefinierte Pfad-Typen:

  • js: js/*.js
  • skin_js: skin/*/*.js
  • js_css: js/*.css
  • skin_css: skin/*/*.css

Quelle: Mage_Page_Block_Html_Head::getCssJsHtml()

Es sind noch weitere Alias-Methoden definiert, die jeweils addItem() mit einem bestimmten Pfad-Typ aufrufen. Folgende Alias-Methoden sind in Mage_Page_Block_Html_Head vorhanden:

  • addCss: ruft addItem mit dem type=skin_css
  • addJs: ruft addItem mit dem type=js
  • addCssIe: ruft addItem mit dem type=skin_css und dem parameter IE
  • addJsIe: ruft addItem mit dem type=js und dem parameter IE

Die local.xml, die dem HTML-Bereich <head> custom.css aus dem Verzeichnis skin/frontend/default/mytheme/css hinzufügt, würde wie folgt aussehen:

<?xml version="1.0"?>
<layout version="0.1.0">

    <default>

        <reference name="head">
            <action method="addItem">
                <param1>skin_css</param1>
                <param2>css/custom.css</param2>
            </action>
        </reference>

    </default>

</layout>

Alternativ kann die Alias-Methode addCss verwendet werden:

<?xml version="1.0"?>
<layout version="0.1.0">

    <default>

        <reference name="head">
            <action method="addCss">
                <param1>css/custom.css</param1>
            </action>
        </reference>

    </default>

</layout>

Die beiden Layout-XML-Codes führen zum gleichen Ergebnis; Sie fügen dem HTML-Bereich <head> auf allen von Magento generierten Seiten die folgende Zeile hinzu:

<head>
...
   <link rel="stylesheet" type="text/css" href="http://domain.tld/skin/frontend/default/mytheme/css/custom.css" media="all" />
...
</head>

Die Alias-Methode addCssIe würde diese Zeilen erzeugen:

<head>
...
<!--[if IE]>
<link rel="stylesheet" type="text/css" href="http://domain.tld/skin/frontend/default/mytheme/css/custom.css" media="all" />
<![endif]-->
...
</head>

Layout-Handles definieren den Kontext, in dem die Layout-Befehle gelten sollen. Das in den Beispielen verwendete Layout-Handle default bezieht sich auf alle Seiten. Neben diesem werden die folgende Handles bei Magento mit zusätzlichem Layout-Code erweitert:

  • catalog_product_view – Alle Produkt-Detailseiten
  • catalog_category_default – Alle Kategorien
  • catalog_category_layered – Alle Ankerkategorien
  • cms_index_defaultindex – Startseite

Mit dem folgenden Layout-XML-Code kann allen Produkt-Detailseiten ein JavaScript aus dem Skin-Verzeichnis des aktuellen Themes mytheme hinzugefügt werden:

<?xml version="1.0"?>
<layout version="0.1.0">

    <catalog_product_view>

        <reference name="head">
            <action method="addItem">
                <param1>skin_js</param1>
                <param2>js/custom.js</param2>
            </action>
        </reference>

    </catalog_product_view>

</layout>

Das Ergebnis wäre die folgende Zeile im HTML-Bereich <head> auf jeder Produktseite:

<head>
...
   <link rel="stylesheet" type="text/css" href="http://domain.tld/skin/frontend/default/mytheme/js/custom.js" media="all" />
...
</head>

Nachbesserung der Rundungsfehlerkorrektur in Magento und PayPal

Rounding Error Fix for Magento and PayPal 1.0.4 auf Magento ConnectRounding Error Fix for Magento and PayPal

Nach der Veröffentlichung unserer Erweiterung, die den Rundungsfehler ausschließlich durch bedingte Erhöhung der Rundungsgenauigkeit korrigieren sollte, wurden uns Konstellationen berichtet, in den der Fehler weiterhin auftritt.

In unserer bisherigen Lösung haben wir die Manipulation der Beträge durch Addition oder Subtraktion des Rundungsfehlers vermieden.

Um sicherzustellen, dass die Warenkorbdaten nach PayPal so übertragen werden, dass die Schlüsselbeträge mit den der Bestellung in Magento übereinstimmen, haben wir uns entschieden nun doch die Formulardaten des PayPal-Moduls zu manipulieren!

Wir erweitern die Methode Mage_Paypal_Model_Api_Standard::getStandardCheckoutRequest, die die Formulardaten für PayPal-Zahlungen bereitstellt. Die Schlüsselbeträge sind die Mehrwertsteuer und der Gesamtbetrag. Da die Nettopreise einzelner Artikel und der Versandkosten nicht direkt in der Magento-Bestelung zu sehen sind, wird die Anpassung dieser Werte nicht auffallen.

Für die Anpassung der Formulardaten gehen wir wie folgt vor:

  • Vergleiche den für PayPal vorbereiteten Gesamtwert mit dem Gesamtwert der Bestellung: Beträgt die Differenz der beiden Werte nicht Null, wird der Rundungsfehler korrigiert. Aufgrund der in Magento bereits vorhandener Logik für die Korrektur des Rundungsfehlers, nehmen wir an, dass der gerundete Rundungsfehler +0.01 oder -0.01 betragen kann.
  • Rekonstruiere die Schlüsselwerte des PayPal-Formulars aus den Bestelldaten: Netto-Gesamtwert der Bestellung, Mehrwertsteuerbetrag, Netto-Versandkosten. Zusätzlich werden noch die Brutto-Versandkosten berechnet, um festzustellen, ob hier ein Rundungsfehler vorliegt.
  • Korrigiere den Rundungsfehler durch Anpassung von: Netto-Versandkosten oder des Netto-Preises des letzten Warenkorbartikels oder des Mehrwertsteuerbetrags. Die Anpassung wird im ersten dieser Schlüsselwerte vorgenommen, bei dem ein Rundungsfehler, berechnet im vorigen Schritt, vorliegt. Aus der Magento-eigenen Rundungsfehlerkorrektur ist uns nicht bekannt, wie der Rundungsfehler des letzten Warenkorbartikels verwendet wird. Deshalb erscheint es uns sinnvoll für die Rundungsfehlerkorrektur den Netto-Preis des letzten Warenkorbartikels anzupassen.

Es folgt die Methode, die die Rundungsfehlerkorrektur implementiert:

public function getStandardCheckoutRequest()
{
    // calculate the rounded request total
    $request = parent::getStandardCheckoutRequest();
    $requestBaseGrandTotal = round($request['amount'] + $request['tax'] - $request['discount_amount'], 2);

    // get the rounded order total
    $orderObj = Mage::getSingleton('sales/order')
                ->loadByIncrementId(Mage::getSingleton('checkout/session')->getLastRealOrderId());
    $orderBaseGrandTotal = round($orderObj->getBaseGrandTotal(), 2);

    // get the rounded rounding error
    $roundingError = round($orderBaseGrandTotal - $requestBaseGrandTotal, 2); // -0.01 or +0.01

    // fix the rounding error
    if($roundingError) {
        $order = array(); // create an array from order data resembling the structure or the request array
        $roundingDeltas = array(); // save the rounding error

        $order['amount'] = round($orderObj->getBaseSubtotal() + $orderObj->getBaseShippingAmount(), 2);
        $roundingDeltas['amount'] = ($orderObj->getBaseSubtotal() + $orderObj->getBaseShippingAmount()) - $order['amount'];

        $order['tax'] = round($orderObj->getBaseTaxAmount(), 2);
        $roundingDeltas['tax'] = $orderObj->getBaseTaxAmount() - $order['tax'];

        $order['shipping'] = round($orderObj->getBaseShippingAmount(), 2);
        $roundingDeltas['shipping'] = $orderObj->getBaseShippingAmount() - $order['shipping'];

        // not contained in the request but useful to determine if there is a rounding error in shipping
        $order['shipping_incl_tax'] = round($orderObj->getBaseShippingAmount() + $orderObj->getBaseShippingTaxAmount(), 2);
        $roundingDeltas['shipping_incl_tax'] = ($orderObj->getBaseShippingAmount() + $orderObj->getBaseShippingTaxAmount()) - $order['shipping_incl_tax'];

        $orderTotalItemCount = $orderObj->getTotalItemCount();

        // hide rounding error in shipping
        if($roundingDeltas['shipping_incl_tax'] && $order['shipping'] > 0) {
            if(isset($request['amount_'.($orderTotalItemCount+1)])) { // ensure that the shipping item is there
                $request['amount_'.($orderTotalItemCount+1)] += $roundingError;
            }
            $request['shipping'] += $roundingError;
            $request['amount'] += $roundingError;

        // hide rounding error in the last cart item
        } elseif($roundingDeltas['amount'] && $order['amount'] > 0) {
            if(isset($request['amount_'.($orderTotalItemCount+1)])) {
                $request['amount_'.($orderTotalItemCount+1)] += $roundingError;
            }
            $request['amount'] += $roundingError;
        } else {
            // hide rounding error in tax
            if($order['tax'] > 0) {
                $request['tax'] += $roundingError;
                $request['tax_cart'] += $roundingError;
            } else {
                // do not correct rounding error in this unexpected situation
            }
        }

    }
    return $request;
}

Rounding Error Fix for Magento and PayPal 1.0.4 auf Magento ConnectRounding Error Fix for Magento and PayPal