Photo Store Stock Verkauf Script (Update)

Verabschiedung des Foto Image Store Scripts

Nach jetzt mehr als drei Jahren ist das sogenannte CMSAccount PhotoVideoStore Script bei unserem Abendballprojekt im Einsatz gewesen. Zwar hat uns das Script immer treu begleitet und hat mehr oder weniger ohne größere Probleme funktioniert, wird es langsam Zeit nach einer anderen Lösung zu suchen. Aber gerade für User, die sich in Sachen Web-Development nicht auskennen, gibt es zur Verabschiedung des Scripts einen kleinen Beitrag über die guten und schlechten Seiten, die wir über die Jahre mit dem Einsatz des Photo Video Store Scripts sammeln konnten.

Zum ersten Beitrag des PhotoVideoStore Scripts

Photo Store Flaw: Image on the fly

Wenn man sich etwas mit Webdevelopment auskennt und dem Script unter die Haube schaut, merkt man sehr schnell, dass dort einige gewagte Programmzeilen vorzufinden sind. Erst einmal als Hintergrundinformation: Man kann sich das bei dem Script so vorstellen, dass beim Hochladen von Bildern entweder ein einfacher Uploader, FTP oder Massenuploader zur Verfügung steht. Während alle drei Methoden Ihre Vor- und Nachteile mit sich bringen geht es dann eher darum, was nach dem eigentlichen Hochladen kommt. Denn egal welche Methode man wählt. Nach dem eigentlichen Hochladen ist ein Foto noch nicht in den Store eingetragen um zum Kaufen, oder zum Download bereit.

Im Interface des Bildershops werden die hochgeladenen Bilder angezeigt und können bequem in einzelne Kategorien gespeichert werden. Der Import geht dabei diverse Schritte nacheinander ab. Das Originalfoto erhält eine inkrementelle ID und wird in einen geschützten Originalordner verschoben. Z.B. also Bild ID 14123, 14124 oder 14125. Zusätzlich wird zum Originalbild in diesem Prozess noch insgesamt zwei Voransichten (Thumbnails) erstellt. Eine dient dabei in der Galerieübersicht als ein Wasserzeichen-geschütztes Vorschaubild mit ca. 400 Pixeln. Die Erstellung sowie Kategorisierung war nach unserem Abendball immer mit einem Aufwand von ca. 3-4 Stunden monotonem einstellen der Kategorien bei einem Bilder-Batch von ca. 50 - bis 100 pro Durchlauf. Die gleiche Anzahl an Bilder wurde pro Durchlauf dann mit Wasserzeichen und Thumbnails generiert. Bei unserem Server hatten wir bei einer größeren Zahl an Fotos dann mit Server-Timeouts zu kämpfen. Da hätte man sich noch etwas mehr Automatisierung gewünscht.

Weiterhin werden noch kleinere Thumbnails erstellt, die in diversen Anzeigen zum schnellen Surfen zwischen Bestellungen vereinfacht. Beide Vorschaubilder nehmen ca. 100 kb pro Foto ein. Bei einer üblichen Datenmenge von 5000-6000 Fotos kann man sich neben den Originalen also nochmal ca. 600 mb Speicherplatz einplanen.

Das ist soweit auch eher das geringste Übel. Irgendwie müssen die Aufnahmen geschützt werden und die Voransichten mit Wasserzeichen auf irgendeine Weise in einer Zwischenspeicherung landen, damit der Webserver nicht bei jedem Aufruf neue Voransichten generieren muss. Soweit ist der Gedanke also gar nicht mal verkehrt gewesen. Bilder die sich nicht Verändern zwischen zu speichern ist gängige Praxis. Aber jetzt kommt das große Scheitern. Der Fehler lag im Detail!

Detailansicht Preview

In der Detailansicht eines Fotos gibt es ein kleines Vorschau Fenster in dem man durch Klicken einer belieben Stelle die Aufnahme mit Wasserzeichen heranzoomt. Das Bild in Groß sieht man dabei zwar nicht, aber mittels verschiedener Ausschnitte kann man inspizieren, ob alles am Foto in Ordnung ist.

Hier und da erhielt man wegen dieser Funktion ein paar Support-Anfragen, man könne das Gesicht aufgrund des Wasserzeichens nicht so gut erkennen. Ganz genau genommen wird das Wasserzeichen aber immer mittig auf das herangezoomte Vorschaubild gelegt. Man könnte also einfach den Bildausschnitt etwas verschieben, um das Wasserzeichen nicht auf dem Gesicht zu haben.

Aber warte mal! Wir haben doch gar keine Thumbnail Bilder für alle beliebigen Bilder in jedem beliebigen Bildausschnitt auf unserem Server zwischengespeichert!

Und genau das ist der Fehler. Denn in Verbindung von einer On-the-Fly Bildergenerierung per PHP wird diese per Javascript dynamisch auf die Anzeige geladen. Im Klartext würde das bedeuten, dass bei jedem Klick auf die Voransicht eine kleines Bildchen vom Original mit Wasserzeichen Overlay erstellt und angezeigt wird. Stellen wir uns vor, dass 100 Besucher sich durch unsere 6000 Fotos zur gleichen Zeit klicken und zwischen Ihren Fotos die Voransichten unter die Lupe nehmen. Das wird zu einem Ressourcenfresser. Wenn da kein guter Server im Einsatz ist, kann man mit Ausfällen rechnen. Positiver Effekt ist eine Ersparnis vom Speicherplatz, da die größte Version mit 400 Pixel nicht viel Platz erfordert. Bei heutigen Webspace-Angeboten ist Speicherplatz aber weniger relevant.

Stockfoto: Anpassbarkeit

Der Shop kommt mit einer Template Engine, wo diverse Designs bereits mit an Board sind. Grundgenommen ähneln sich alle in der Anzeige. Nur Header und Footer sind leicht verändert. Unverständlich wird da sofort, dass der Footer in verschiedenen Dateien Inline programmiert wurde. Ändert man diesen an einer Stelle, wird er allerdings nicht an einer anderen geändert.

Das ist auch nur eines von vielen Punkten, die nicht gerade Flexibel waren. Einige kleinere Änderungen in der Anzeige gestalteten sich aufgrund von schlechter Programmierung als mühevoll und schwierig. Ein fataler Fehler war zum Beispiel, dass bei einer Bestellung per manueller Überweisung keine Bestellbestätigung mit Überweisungsdaten, oder Rechnung versendet wurde. Diese wurden lediglich beim nach dem Bestellvorgang kurz angezeigt.

Die Kombination aus Funktionen und E-Mail Templates ist dann noch so verschachtelt und verzwickt, dass man auch hier sehr viel Zeitaufwand hat, um eine minimale Änderung vorzunehmen. Anpassungen kann man also vergessen. Ich kann mich noch sehr gut daran erinnern, wo ich an einer Nacht nach einer Änderung am Script auf einmal ca. 1200 E-Mails an mich selbst verschickt habe.

Meiner Meinung nach sollte sich jemand mit Interesse an dem Script, über das Demo einen Einblick verschaffen. Wenn alles so funktioniert, wie man das gerne hätte, muss man mit dem Photostore so leben, wie er vom Hersteller kommt. Sobald der Workflow nicht passt, sollte man die Zeit, oder das Geld in eine andere Lösung investieren.

Photostore Script: Viele Funktionen, nichts gescheites.

Der Funktionsumfang des Scripts ist extrem Groß. Angefangen von Registrierung, User und Verkaufssystem mit Provisionen gibt es in dem Photo Store Script noch verschiedene Zahlungsmöglichkeiten, die man sich aktivieren kann, Image to Print Schnittstellen, Digital Rights Management und vieles was man nicht braucht. Auch Funktionen wie Blog, Support, News und Profile helfen da nicht. Bei diesem Script kann man leider nicht sagen, dass es genau so, genau das macht, was man von einem Photo Store Script erwartet.

Vergleichen wir es doch mal mit einer Foren-Software. Wir haben bereits eine Webseite - Blog, News, Faq, usw. ist alles vorhanden, brauchen allerdings noch ein Forum wo sich Kunden, Interessenten und Fans mit Fragen/Antworten unterhalten können, dann suchen wir eine Lösung, was dieses Problem für uns löst. Nicht eins, was alle Probleme versucht zu lösen. Genau das Gefühl kann man von dem CMSAccount Shop spüren.

Bilder per Paypal kaufen

Wir hatten auch die Integration von Paypal in dem Image-Shop aktiviert. Generell wie man es nicht anders erwartet, wird so eine Bestellung direkt von Paypal geprüft und bestätigt. Im Falle von digitalen Downloads (also keinen Prints) würden diese in der Bestellung sofort freigeschaltet.

Eine PayPal Integration wird für verschiedene Programmiersprachen direkt von PayPal gestellt. Das würde ich schon gar nicht als Eigenleistung zählen. Als kurze Darstellung kann man sich das so vorstellen, dass eine Bestellung über ein HTML-Formular Informationen zur Bestellung - also Warenkorb aus Bilder und Preise per POST-Request zum PayPal Server sendet. Die Zahlung wird direkt auf PayPal durch deren System durchgeführt und eine Rückantwort wird an eine ausgewählte URL zurück an den Server geschickt, die mittels Antwort eine Bestellung nur freischaltet nachdem eine Prüfung stattgefunden hat. Hört sich kompliziert an, ist allerdings auch neben der Schnittstelle, die von Paypal bereitgestellt wird, nur wenige Zeilen Code.

Wir hatten von allen Bestellungen vielleicht die hälfte davon als PayPal Bestellungen. Diese waren zum größten Teil zuverlässig und sofort freigeschaltet. Dabei haben sich leider ein paar Fehlbestellungen eingeschlichen, in denen neben der eigentlichen Bestellung eine weitere mit leerem Warenkorb vorzufinden war. Manchmal sogar zwei oder drei. Da ging dann das ganze Prozedere los. In der Übersicht unter Bestellung 123, 124 und 125 immer die gleichen Kundeninformationen, zweimal mit Summe NULL - bzw. ein leerer Warenkorb. Auch wurden dann jeweils Bestellbestätigungen versendet. Das Unterfangen dieser fehlerhaften Bestellungen zu löschen, dem Kunden vielleicht noch auf eine E-Mail zu Antworten war dann auch wieder sehr zeitaufwendig.

PHP + MySQL unter der Haube

Laut Verkaufsseite des Scripts erhält man nach Kauf eine Berechtigung auf Updates und Verbesserungen innerhalb von einem Jahr. Da wir inzwischen schon mehr als drei Jahre zählen, können wir das wahrscheinlich nur entgeltlich verlangen. Wir hatten das Script die ganze Zeit auf PHP 5.6.x laufen und konnten recht wenige Fehler feststellen. Jetzt ist 5.6. kurz vorm End-of-Life Cycle und wird in kurze vom Support und Development eingestellt. Bis dahin sollte man dann sowieso auf php 7 umgestellt haben.

Auf einem lokalen Testserver haben wir das Script bereits auf Tauglichkeit und Performance von php 7 getestet. Um es da ganz Kurz zu sagen, ist unsere Version für php 7 nicht kompatibel. Ein weiterer Grund nach einer anderen Lösung zu suchen.

Da alle Daten von Downloads, Bestellung, Bestellungs-Details und Kundendaten in einer MySQL Datenbank gespeichert sind, haben wir auch diese ausgelesen und uns angeschaut, wie sauber und aufgeräumt das Script in Sachen Datenbank läuft.

Auch da war es unnötig Kompliziert und undurchsichtig. Um Daten auf ein anderes System zu exportieren mussten wir uns durch viele zusammenhängende Tabellen wuseln, um den Datensatz für weitere Applikationen zu konzipieren. Bestellungen hatten über eine ID eine Verknüpfung zu Bestellung-Details. Die Details hatten wiederum eine Verknüpfte ID zu Bild Items, sowie User-Daten.

Auch wurde bei einem Kauf erst ein Download Link erstellt, der in Verbindung von einem Zeit oder Download Limit ungültig wurde. Aber es gibt so viele andere Möglichkeiten, ein Script zu gestalten, um ein Download bereit zu stellen. Es muss weder die Datei Versteckt werden, noch ein kryptischer Link dafür in einer Datenbank gespeichert werden.

Mit ganz einfachen Funktionen könnte man in Verbindung der Bestellung einfach den Warenkorb durchlaufen und falls die jeweilige Datei im bezahlten Warenkorb vorhanden ist, einen Download erzwingen. Im Vergleich zu sehr langen nutzlosen Funktionen hätte man dieses Problem innerhalb von wenigen Zeilen geklärt. Hier ein Beispiel:

Codebeispiel vom Photostorescript:

Man sieht zuerst einmal die Readability vom Code. Download klicken und los gehts, falsch gedacht! Diverse Datenbankabfragen werden in den nächsten Zeilen unten gemacht, damit das Bild schließlich am Ende des Codes schließlich startet. Debugging und Fehlerbehebungen ist wie eine Nadel im Heuhaufen.

$sql="select id_parent,link,data,tlimit,ulimit from downloads where link='".result3($_GET["f"])."' and data>".mktime(date("H"),date("i"),date("s"),date("m"),date("d"),date("Y"))." and tlimit<ulimit+1";
    $ds->open($sql);
    if(!$ds->eof)
    {
        $sql="update downloads set tlimit=tlimit+1 where link='".result3($_GET["f"])."'";
        $db->execute($sql);
        $_GET["u"]=link_download($ds->row["id_parent"],1);
        $server1=link_download($ds->row["id_parent"],2);
$sql="select id,name,url,price_id,id_parent from items where    id=".$ds->row["id_parent"];
        $dn->open($sql);
        if(!$dn->eof)
        {
$sql="select * from sizes where id_parent='".$dn->row["price_id"]."'";
            $rs->open($sql);
            if(!$rs->eof)
            {
                $width=$rs->row["size"];
                $fld=explode("/",$_GET["u"]);
                if(!$flag_storage)
                {
                    $size = getimagesize($DOCUMENT_ROOT.$server1."/".$_GET["u"],$info);
                    if($size[1]>$size[0])
                    {
                        $width=$size[0]*$width/$size[1];
                    }
// (...) 
// Viele Funktionen habe ich zur Illustration weggelassen...
// Unten Download Funktion
                ob_clean();
                header("Content-Type:image/jpeg");
                header("Content-Disposition: attachment; filename=".str_replace(" ","%20",$dn->row["url"]));
                ob_end_flush();
                @readfile($photo_file);
                exit();

Die Download Funktion selbst ist eigentlich nach einem gängigen Schema, in diesem Beispiel sieht es allerdings so aus, dass diese in diesem Beispiel nur für JPG / JPEG Bilder ausgelegt ist, alles was vor der Downloadfunktion passiert, muss erst einmal Aufwendig entziffert werden.

Meine Implementation

Im Kontrast dazu habe ich hier ein funktionierendes Beispiel auf Grundlage eines selbst-programmierten Image-Store angefügt und mit hilfreichen Kommentaren ausgestattet. Wie ich finde, wäre es für einen Außenstehenden wesentlich einfacher zu verstehen, wenn dieser eine Änderung durchführen möchte. Auch ist die Funktion wesentlich kürzer. Das ganze ist natürlich genau so sicher und ohne Kompromisse.

<?php 
       // Einfaches Überprüfen, ob ein Bild in einer erfolgreichen Bestellung vorhanden ist
       // Param = URL Parameter, wird Aufgerufen wenn ein Kunde auf den Download Button in der Bestellung klickt.
       // isVisible = Methode, die Prüft ob die Bestellung Freigeschaltet wurde (Freischaltung wird automatisch durch Zahlungsgateway oder Manuell nach Überweisung getätigt
    if(param('download') && $page->isVisible()){
       // "Für alle im cart" = geht durch alle Zeilen im Warenkorb
        foreach($page->cart()->toStructure() as $cart){
       // Download Parameter beinhaltet dateiname,  es wird geprüft ob die Zeile im Warenkorb damit übereinstimmt
            if(param('download') == page($cart->category())->image($cart->image())->filename()){
       // Der Download wird gestartet.
                page($cart->category())->image($cart->image())->download();
            }
        }
    } ?>

Während dieses Beispiel nur ein ganz kleines Eindruck verleiht, womit man sich beschäftigen muss, um Anpassungen durchzuführen, kann man sich das auf alle Funktionen hoch-multiplizieren. Bei anderen Funktionen des Photostore Scripts sieht es nicht besser aus.

Ist das Storescript zu empfehlen?

Trotz der genannten Beispiele, hat das Script im großen und ganzen Zuverlässig funktioniert. Wer das nötige knowhow nicht mit bringt und vom Design und Funktionsumfang mit dem Script gut klar kommt, kann dieses für einen günstigen Anschaffungspreis nutzen. Sobald man auf die Ebene kommt, dass Funktionen so automatisch und genau wie nach dem eigenen Workflow ablaufen sollen, dann muss man eine andere Lösung suchen.

Wir waren für unseren Einsatz damit auch zufrieden. Aufgrund der ausgelaufenen Version und ohne php 7 Support unserer Version, gehen wir in kürze getrennte Wege. In Zukunft haben wir Zeit und Geld auf eine eigene Lösung gesetzt, um unseren Workflow soweit wie möglich zu Automatisieren. Dabei müssen wir allerdings auf viele Funktionen verzichten, die im Photostore Script inkludiert waren, aber wir zum größten Teil sowieso nicht nutzen.

Vielleicht stellen wir euch die neue Lösung vor, sobald die Migration vom alten Script durchgeführt wurde!

Kontaktieren Sie uns jetzt!
Anderen über Photo Store Stock Verkauf Script (Update) erzählen

Was würden Sie über dieses Thema wissen wollen? ( 0)

* = Partnerlink