Indicate Models: Eine Umsetzung mit “Liquid Design”

Neuerstelltung der Internetpräsenz für die Münchner Modelagentur “Indicate-Models”.

Hervorstechendes Merkmal ist hier die edle und schlicht gehaltene Gestaltung, welche sich nach dem Prinzip des “Liquid-Design” über jede Seitenbreite erstreckt und dem hohen visuellen Anspruch der Zielgruppe genügt.

Charakteristisch für das Liquid Design ist, dass sich der Seitenaufbau ausschließlich an der Größe des Bildschirms des Besuchers orientiert und nicht in einen festen Rahmen (Viewport) gezwängt ist.

Im Backend müssen die Anwender lediglich neue Models angelegen, diese werden komplett selbständig über die Seiten verteilt.

Durch die Verwendung des mächtigen Open Source Content-Management Framework Pimcore, wurde es der Agentur ermöglicht, sämtliche Models eigenständig mit Hilfe anwenderfreundlicher Eingabeformulare anzulegen und zu pflegen.

Langfristig sollen in diesem Framework über 500 Models verwaltet werden.

Captchas von Zend Forms in individuelle Formulargestaltung integrieren.

In Formulare in Pimcore einfach erstellt hatten wir ja schon erläutert, wie man ein Formular “zu Fuss” programmiert und sich nur bei den benötigten Elementen bedient, um so das Formular ganz individuell ohne den Aufwand eigener Decorator zu erstellen.

Problem bei der Herangehensweise ist aber, dass Captcha Elemente keine renderViewHelper() Methode kennen und nur über render() ausgegeben werden können. Hierbei werden dann die default-Decorators benutzt, die man dann mit eigenen ersetzen müsste.

Um nun alle Elemente des Captchas (Captcha-Bild, Eingabefeld und hidden-field mit “Lösung”) auszugeben muss man die Schritte ausführen, die Zend_Form_Decorator_Captcha_Word mittels render() erledigen würde:

    public function render($content)
    {
        $element = $this->getElement();
        $view    = $element->getView();
        if (null === $view) {
            return $content;
        }

        $name = $element->getFullyQualifiedName();

        $hiddenName = $name . '[id]';
        $textName   = $name . '[input]';

        $label = $element->getDecorator("Label");
        if($label) {
            $label->setOption("id", $element->getId()."-input");
        }

        $placement = $this->getPlacement();
        $separator = $this->getSeparator();

        $hidden = $view->formHidden($hiddenName, $element->getValue(), $element->getAttribs());
        $text   = $view->formText($textName, '', $element->getAttribs());
        switch ($placement) {
            case 'PREPEND':
                $content = $hidden . $separator . $text . $separator . $content;
                break;
            case 'APPEND':
            default:
                $content = $content . $separator . $hidden . $separator . $text;
        }
        return $content;
    }

Interessant sind hier für uns die Codeteile, die sich mit dem Text- und Hiddenfield befassen.
Diese bedienen sich anderen View Helpern, nämlich Zend_View_Helper_FormHidden und Zend_View_Helper_FormText, die nur mit geeigneten Parametern wie dem Feldnamen oder zu vererbenden Attributen versorgt werden müssen.

Hier ein Beispiel, wie man die Darstellung behandeln kann:

<?php
$formbuilder = new Formbuilder_Frontend();
$form = $formbuilder->getForm("kontakt");
$captcha = $form->getElement("captcha");
$name = $captcha->getFullyQualifiedName(); // Der Name des Eingabefelds
$hiddenName = $name . '[id]'; // Der Name des hidden-fields, erweitert als Array
$textName   = $name . '[input]'; // Der Name des text-fields, erweitert als Array
?>

        <tr>
            <td><?php print $form->captcha->renderLabel() ?></td>
            <td>
              <?php print $form->captcha->renderCaptcha() ?><br>
              <?php print $this->formHidden($hiddenName, $captcha->getValue(), $captcha->getAttribs()); ?>
              <?php print $this->formText($textName, '', $captcha->getAttribs());  ?>
              <?php print $form->captcha->renderErrors() ?></td>
        </tr>

Karriereseite der Käserei Champignon online gegangen

Diese Woche ist der Relaunch der Karreiereseite der Käserei Champignon online gegangen.
Beauftragt von der Trio Group München – Agentur für intensive Markenkommunikation waren wir verantwortlich für die Umsetzung der Design- und Textvorlagen. Die neue Seite demonstriert mit einem konsistenten Design eine starke Anbindung an die Unternehmenswebseite der Käserei Champignon und bietet Informationen über die Arbeit und Ausbildung an den Unternehmensstandorten sowie zu angebotenen Studiengängen. Zudem werden die gerade offenen Stellen und Praktika gelistet, auf die man sich auch unmittelbar online bewerben kann.

Größtes Augenmerk wurde von uns dabei darauf gelegt, die Seite so barrierefrei wie möglich zu realisieren um sie so einem umfassenden Publikum zugänglich machen zu können. Die damit einhergehende saubere semantische Auszeichnung wird dabei als angenehmer Nebeneffekt von den Suchmaschinen sehr positiv aufgenommen.

Realisiert wurde das Projekt mit dem mächtigen Open Source Content-Management Framework Pimcore, das auf offenen Standards, dem ZEND Framework und ExtJS basiert. Pimcore ermöglichte es uns durch seine große Flexibilität und mächtigen Features das Projekt dem sportlichen Zeitplan, der unser vorgelegt wurde, gerecht zeitnah umzusetzen.

Relaunch www.champignon.de erfolgreich durchgeführt.

Die neue Unternehmensseite der Käserei Champignon http://www.champignon.de/ ist erfolgreich online gestellt worden.


Beauftragt durch die Agentur trio-group münchen wurden die Design- und Textvorlagen durch uns mit dem preisgekrönten Content Management System pimcore umgesetzt.
Interessante Informationen für den Verbraucher wie eine Kriterien basierte Suche im Sortiment der Käserei, eine Rezeptdatenbank mit Eingrenzung auf die gewünschten Käsesorten oder andere Kriterien sowie umfangreiche Informationen zum Unternehmen und seines Sortiments sind gepaart mit Mehrsprachigkeit, einer Volltextsuche und anderen Features.

Pimcore ist ein mächtiges Content-Management Framework basierend auf offenen Standards, dem ZEND Framework und ExtJS. Zudem ist Pimcore das erste und derzeit einzige Open Source Enterprise Product Information Management Framework. Als eingetragener Pimcore Professional Partner konnten wir die Stärken des Systems für unseren Kunden voll zur Geltung bringen.

Das Zend Form Builder Plugin und Pimcore 1.4.5

EDIT: Das Plugin ist mittlerweile upgedated.
Diese Artikel hat damit nur eine eine dokumentierende Absicht.

Seit dem Release von Pimcore 1.4.5 funktioniert das Form Builder Plugin nicht mehr.
satsume aus dem Pimcore Forum ist es gelungen, den Fehler zu finden und eine passende Problemlösung bereitzustellen.

Am 12.04. wurde Ext.ux.form.SuperField aus den Libraries, die mit Pimcore ausgeliefert werden, entfernt.

Bis es einen offiziellen Fix gibt kann man sich damit behelfen, diese Dateien dem Plugin selbst bereit zu stellen und in der plugin.xml bekannt zu machen.

Wir haben das schon gemacht und stellen das modifizierte Plugin hier zum download zur Verfügung: Zendformbuilder_for_1.4.5.tgz

Zur Installation das Archiv einfach im plugins/ Verzeichnis entpacken – das Plugin ist komplett, es werden daher alle vorhandenen Dateien überschrieben, es eignet sich daher auch zu Erstinstallation.

Mit diesem Archiv wurden folgende Probleme gefixt:

  • fehlende SuperField Extension seit Pimcore Version 1.4.5
  • fehlende application_form_add.png
  • Funktionsaufruf exec() in Plugin.php ist mit DB-Treiber mysqli unzulässig, es sollte query() verwendet werden.
  • in isInstalled() wird nun eine generelle Exception abgefangen da ihr Typ vom verwendeten Treiber abhängt

Pimcore in Version 1.4.5 erschienen

Pimcore ist in Version 1.4.5 erschienen

Neben vielen Bugfixes bietet das neue Minor Release auch neue und interessante Features:

  • register shutdown function hook
  • Website-Error Log
  • less.js errormessages in edit mode
  • Tag & Snippet Management
  • Ability to add your own attributes to a link.
  • Drag&drop possibility to change the order of Elements in Documents
  • translateAdmin has the same features as translate

Formulare in Pimcore einfach erstellt

Pimcore liefert mit dem unterliegenden Zend Framework und dem Formbuilder-Plugin von Alexandre Delattre eine komfortable Möglichkeit, eigene Formulare zu erstellen und zu behandeln.

Die Zend Forms sind die eierlegende Wollmilchsau, die alles bietet von der Definition der Felder selbst über die Verifizierung der Inhalte und ihrer Bereinigung bis zu Ihrer Verarbeitung – und, nicht zuletzt, ihrer Darstellung im Browser.
Diese Darstellung erfolgt mit dem Strukturmuster der Decorator, jedes Element “weiss”, wie es dargestellt werden sollte und das Formular kann “sich selbst” ausgeben. Das ist sehr angenehm und praktisch, da für einfache Anwendungen keine weitere Schritte unternommen werden müssen als ein <?php print $form; ?>. Hier liegt aber auch die Krux, denn wirklich aufwändige Designs umzusetzen erfordert einen unverhältnismäßig hohen Aufwand zur Definition der oftmals für jedes Element individuellen Decorator.

Man kann aber auch den umgekehrten Weg gehen und das Formular “zu Fuss” programmieren und sich nur bei den benötigten Elementen des Formulars bedienen.
Genau das soll in diesem Beispiel geschehen, zusammen mit ein paar anderen Tricks und Kniffen.

Zuerst muss natürlich das Formular im Formbuilder Plugin gestaltet werden. Hierzu fügt man einfach die benötigten Form Elemente zusammen und stattet diese mit sinnvollen Validatoren zur Prüfung erlaubter Eingaben und Filtern zur Entfernung unerwünschter Inhalte aus.

Ist das Formular erstellt und gespeichert, kann man es im Controller seiner View zuweisen:

public function uploadAction () {
  $formbuilder = new Formbuilder_Frontend();
  $form = $formbuilder->getForm("onlinebewerbung");
  $this->view->form = $form;
}

In der View könnte man es nun theoretisch einfach nur ausgeben und die Ausgabe wäre damit erschöpfend behandelt:

<?php print $this->form; ?>

Hier wollen wir aber eingreifen und jedes Feld per Hand in individuellem HTML/PHP-Code ausgeben. Im Wesentlichen besteht ein Element hierfür aus den Komponenten Label, also der Feldbeschriftung, ViewHelper für die Darstellung des eigentlichen Eingabelemenents und dem Error für den Fall, das ein Validator eingreift.

Die Ausgabe erfolgt nun über entsprechende Methoden:

<?php $form = $this->form; ?>
<?php print $form->anrede->renderLabel() ?>  <?php print $form->anrede->renderViewHelper() ?><?php print $form->anrede->renderErrors() ?>

Was am Label nicht per Default dranhängt ist die Anzeige ob die Eingabe eines Feldes erforderlich ist. Der Label-Decorator kennt aber ein “requiredSuffix” mit dem man bestimmen kann, was das Label in diesem Fall rendern soll.

Nun muss man nur noch das ausgefüllte Formular verarbeiten. Dabei bedienen wir uns eines Tricks um die Code-Workflow übersichtlich und gering zu halten.
Wird die Seite einfach nur so (also per GET) aufgerufen, liegt in der Regel keine Wertübermittlung vor und das Formular soll nur ausgegeben werden.
Als action gibt man im form nun die URL des forms an – es ruft sich also selbst auf, vorzugsweise mit einem POST! Diesen POST kann man nun als Signal für die Weiterverarbeitung auswerten; if ($this->getRequest()->isPost()). Das Formular muss nun auf korrekte Eingabgen geprüft werden: if ($form->isValid($_POST)) {.
Bei dieser Prüfung setzt Zend_Form, falls nötig, die entsprechenden Fehlermeldungen. Nur bei erfolgreicher Prüfung verarbeitet man also weiter, kommt die Prüfung mit dem Ergebnis false zurück soll der Code wieder die Darstellung übernehmen – nur mit dem Unterschied, dass das Form mit den notwendigen Fehlermeldungen ausgestattet wurde, die nun mittels renderErrors() im View dargestellt werden.
War die Prüfung dagegen erfolgreich wird die Eingabe geeignet verarbeitet und die Ausführung auf einer Folgeseite mittels _redirect($url) fortgesetzt.

public function uploadAction () {
  $formbuilder = new Formbuilder_Frontend();
  $form = $formbuilder->getForm("onlinebewerbung");

  // Das Formular verarbeiten wenn es per POST übermittelt wurde
  if ($this->getRequest()->isPost()) {

    // Ist die Eingabe valide?
    if ($form->isValid($_POST)) {
      $values = $form->getValues(); // Alles OK, die Werte abholen

      // Hier nun was damit anfangen

      $this->_redirect("/danke"); // Redirect an die Folgeseite
      // Hier kommen wir nun nicht mehr hin!
    }
  }
  // Das Formular wird zum ersten Mal aufgerufen oder die Eingabe war nicht valide

  // Die Elemente, die ausgefüllt werden müssen,
  // mit einem "*" nach dem Label rendern
  foreach ($form->getElements() as $formelement) {
    $decorator = $formelement->getDecorator('label');
    if (is_object($decorator)) {
      $decorator->setOption('requiredSuffix',' *');
    }
  }
  $this->view->form = $form;
}

Ein paar Klippen gilt es vielleicht noch zu umschiffen. Das Element für Fileuploads kennt keine renderLabel() Methode, man kann es nur vollständig (inklusive Label) mit render() ausgeben lassen. Muss man gewisse Designvorgaben umsetzen und z.B. nach dem Label einen “:” ausgeben, kann man den Label-Decorator entsprechend konfigurieren:

foreach ($form as $element) {
  $label = $element->getDecorator("Label");
  if (is_object($label)) $label->setOption("separator", ":"); // nach jedem Label einen : rendern.
}

Auch lassen sich leider Captchas nicht “zerlegen” sondern nur komplett rendern, eigene Umsetzungen sind hier nur schwer möglich – dem widme ich aber noch einen eigenen Blogeintrag :-)

Pimcore mit Hoster United Domains

Um Pimcore beim Hoster United Domains installieren zu können müssen ein paar Hürden überwunden werden.

Erstes Problem ist, dass die Datenbank nicht auf UTF-8 eingestellt ist – was sich mit einem selbst installieren phpMyAdmin oder Navicat aber leicht beheben lässt.

Zudem ist aus Sicherheitsgründen bei UD der Einsatz der Funktionen set_time_limit() und ini_get_all() gesperrt, was das Installieren bzw. Betreiben von Pimcore verhindert. Hier müssen zwei kleine Workarounds gebastelt werden:

In pimcore/modules/install/controllers/IndexController.php:

        $maxExecutionTime = 300;
        @ini_set("max_execution_time", $maxExecutionTime);
        set_time_limit($maxExecutionTime);

Hier muss vor das set_time_limit ein “@” um die Ausgabe der Warning zu unterdrücken:

        $maxExecutionTime = 300;
        @ini_set("max_execution_time", $maxExecutionTime);
        @set_time_limit($maxExecutionTime);

Ähnliche Änderungen sind, je nach Version von Pimcore, auch in pimcore/lib/Pimcore.php in Methode setSystemRequirements() notwendig:

        @set_time_limit($maxExecutionTime);

Wenn das Aufrufen nur mit einem ERR_CONTENT_DECODING_FAILED order ähnlichem seitens des Browsers quittiert wird, sollte man das Encoding abstellen, da ein Fehler oder eine Warning passiert, bevor tatsächlich encodeter Inhalt ausgeliefert wird. Dieser Inhalt kann dann nicht decoded werden (da ja nicht encoded) und der Browser wirft einen Fehler.
Leider kann das Abschalten nicht konfiguriert werden, also muss man auch kurzzeitig per Sourcecode-Änderung ran:

    public static function outputBufferEnd ($data) {

        $contentEncoding = null;
        if( strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'x-gzip') !== false ) {
            $contentEncoding = 'x-gzip';
        } else if( strpos($_SERVER["HTTP_ACCEPT_ENCODING"],'gzip') !== false ) {
            $contentEncoding = 'gzip';
        }

	// Dies ist die Änderung, die nach dem Debugging wieder entfernt werden sollte:
        $contentEncoding = null;

Als nächstes muss noch im bei Pimcore beigelegten Zend Framework was geändert werden.
Dadurch, das ini_get_all() deaktiviert ist, muss man der Klasse Zend_Session explizit sagen, welche ini-Einträge ausgelesen werden sollen:

    public static function getOptions($optionName = null)
    {
        $options = array();
        foreach (ini_get_all('session') as $sysOptionName => $sysOptionValues) {
            $options[substr($sysOptionName, 8)] = $sysOptionValues['local_value'];
        }

muss geändert werden zu (beispielsweise):

    public static function getOptions($optionName = null)
    {
        $options = array();
				$ini = array("session.auto_start","session.bug_compat_42","session.bug_compat_warn","session.cache_expire","session.cache_limiter","session.cookie_domain",
						"session.cookie_httponly","session.cookie_lifetime","session.cookie_path","session.cookie_secure","session.entropy_file",
						"session.entropy_length","session.gc_divisor","session.gc_maxlifetime","session.gc_probability","session.hash_bits_per_character",
						"session.hash_function","session.name","session.referer_check","session.save_handler","session.save_path","session.serialize_handler",
						"session.use_cookies","session.use_only_cookies","session.use_trans_sid");

				foreach ($ini as $sysOptionName) {
					$sysOptionValues = ini_get($sysOptionName);
					$options[substr($sysOptionName, 8)] = $sysOptionValues['local_value'];
				}

Wir haben diese Werte auf einem lokalen Server pragmatisch durch durch php_info() ermittelt – ob das für alle Zeiten so richtig bleiben wird muss gegebenenfalls jeder für sich selbst herausfinden.

Edit vom 29.04.2013:
- Encoding-Debugging eingebaut
- max-exeution-time in Pimcore.php berücksichtigen