Symfony: “Unable to find template”

Die Überschrift ist etwas irreführend. Es geht darum, dass referenzierte Templates nach dem Muster

bundle:section:template.format.engine

aufgebaut sein müssen. Der Teil “template” muss (?) dabei den relativen Pfad enthalten, zB

MyProjectBundle::pages/testpage.html.twig

, andernfalls kommt es zu der Fehlermeldung aus der Überschrift. Nun will man vielleicht nicht an drölf Stellen diesen Pfad (oder das Bundle!) angeben (sei es der Zentralisierung wegen, sei es aus Faulheit beim Tippen). Lösung: FilesystemLoader, bzw. das geerbte Twig_Loader_Filesystem->addPath(…):

bzw. im Template:

TYPOlight: Hooks nutzen, um ZIP-Files automatisch zu entpacken

Ein Hook ist simpel: Von der bekannten Modulstruktur benötigt man nur die /config/config.php, in der man Klasse und Funktion an den gewünschten Hook hängt:

Sowie die Klasse als solche (direkt im Modulordner):

Das war’s im Wesentlichen. Der Vollständigkeit halber hier noch die Funkion zum Entpacken, sowie ein wenig Aufräumen der hochgeladenen Dateien:

HTH

TYPOlight: Eigene Content-Elemente

Direkt analog zur Erweiterung bestehender Content Elemente mit neuen Feldern funktioniert das Erstellen eigener Content Elemente. Man hängt sich nur in andere (bzw. weitere) Arrays. Ich orientiere mich im Folgenden an diesem Tutorial, sowie an dieser Erweiterung.

Also, gegeben sei dieselbe Dateistruktur wie für neue Felder, in einem eigenen Modul. In /config/config.php deklariert man sein neues Content-Element:

In /config/database.sql fügt man die gewünschten Datenbank-Felder hinzu:

In /dca/tl_content.php ergänzt man die neuen Felder und deklariert die Palette:

In languages/de/tl_content.php und analog in languages/en/tl_content.php ergänzt man die referenzierten Texte:

Nun kommt die neue Klasse IframeCustom:

Und schließlich das Template ce_iframe_custom.tpl:

Ein Selbstgänger 🙂

TYPOlight: Content-Elemente mit eigenen Feldern

[Disclaimer: Ja, TYPOlight heißt schon einige Zeit Contao – die Installation, die ich hier vor mir habe, heißt aber noch TYPOlight. Aktuell ist 3.3.3, Beta ist 4.0, ich habe 2.8.1. Eine Übersicht über alle Versionen gibt es hier.]

Es gibt eine Doku zum Hinzufügen von Feldern. Mein Problem damit: Die Anleitung bezieht sich auf die Mitgliederverwaltung. Die Mitgliederverwaltung hat aber eine eigene Tabelle (tl_member) in der Datenbank, sowie eine eigene Palette. Interessanter ist es, eine neues Feld bsplw. dem Content Element “Image” hinzuzufügen, den alle Content Elemente teilen sich die Tabelle tl_content, sowie eine gemeinsame Palette. Wie bekommt man ein Feld, das nur für Images angezeigt wird?

Analog zum oben verlinkten Beispiel: Über ein Modul, und zwar mit folgender Dateistruktur:

  • tl_content_custom/
    • config/
      • config.php
      • database.php
    • dca/
      • tl_content.php
    • languages/
      • de/
        • tl_content.php
      • en/
        • tl_content.php
    • templates/
      • ce_image_custom.tpl
    • ContentImageCustom.php

Im ersten Schritt hängt man sich in das CMS:

In config/config.php registriert man eine eigene Klasse für den Typ “Image”:

In config/database.sql definiert man die zusätzlich benötigten Felder der Tabelle tl_content – und man darf sich nicht daran stören, dass das in einem CREATE TABLE passiert:

Die Felder werden dann über http://mydomain.tld/typolight/install.php unter dem Punkt “Tabellen aktualisieren” aktualisiert; dort wird das CREATE in ein ALTER geändert.

In dca/tl_content.php deklariert man, wann das neue Feld angezeigt wird (“dca” steht für “Data Container Array“), also dass es nur für Images verwendet werden soll. Dazu ergänzt man “die Palette” und “die Felder”. Die Palette ist quasi ein Sub-Set aller für Content-Elemente verfügbaren Felder. Wenn wir also das Content Element “Image” nur um vorhandene Felder erweitern wollten, dann bräuchten wir die Felder nicht ergänzen. Das ist hier nicht der Fall (wir haben oben ein neues Feld in die Datenbank eingefügt), deshalb konfigurieren wir das neue Feld is_retina und hängen es (ja: per str_replace ^^) an die kommaseparierte Liste von erlaubten Feldern für den Typ “Image”. Dabei kann man sich an /system/modules/backend/dca/tl_content.php orientieren, dort sind die Original-Deklarationen der Felder und Paletten zu finden.

In languages/de/tl_content.php und analog in languages/en/tl_content.php kommen alle benötigten (=in der Feldkonfiguration referenzierten) Texte, das ist hier nur einer:

Im zweiten Schritt erstellt man dann endlich die eigene Logik. In config/config.php haben wir die Klasse ContentImageCustom referenziert (s.o.). Die wird jetzt direkt im Modulordner angelegt; von dort wird sie automatisch geladen. Der Einfachheit halber erweitern wir ContentImage, und der einzig nötige Code ist die Referenzierung unseres eigenen Templates, in dem wir dann auf das neue Feld reagieren:

Das genannte Template ist templates/ce_image_custom.tpl, und darin sieht’s dann so aus:

(Das ist natürlich nur ein Snippet; insgesamt habe ich den Code von /system/modules/frontend/templates/ce_image.tpl geklaut, und mit obigem Snippet erweitert.)

HTH

CodeIgniter: Emailversand über SMTP

Wie oft habe ich schon die Email-Klasse verwendet, nur um festzustellen, dass sie nicht funktioniert? Sicher… zwei mal 🙂 Deshalb: Sie funktioniert nicht. Stattdessen verwende ich PHPMailer, bzw. folgende Dateien davon:

Eingebunden werden die ein einem Helper (/application/helpers/email_helper.php):

mit entsprechender Config (/application(config/email.php, wird automatisch eingebunden):

Aufruf dann via:

HTH

Facebook API: Hochauflösende valide Image-URLs

Guten Tag.

Über die FB-API bekommt man vergleichsweise kryptische Image-URLs, nämlich die Direktlinks in deren CDN. Diese haben drei Probleme:

1. Die URL zeigt auf die gering aufgelöste Version des Bildes, zu erkennen am “_s.jpg” (“s” für “small). Der gängige Ansatz, um an die hochauflösende Version zu kommen, besagt, dass man “_s” durch “_n” (für “normal?) zu ersetzen (einige empfehlen auch “_o” für “original”). Das funktioniert meistens, aber eben nicht immer. Korrekt scheint dieser (selbstverständlich undokumentierte) Ansatz, der quasi analog zur Profilbild-URL funktioniert:

<object_id> kommt aus der API. Achtung: Das funktioniert nur für Posts vom Typ “photo”; Posts vom Typ “link” müssen nach wie vor über die URL im Feld “picture” gehen! Und damit kommen wir direkt zum nächsten Problem:

2. Facebook nutzt Proxy-Skripte zur Generierung der niedrig aufgelösten Bilder. Zu erkennen an …/safe_image.php?…&url=<url_codiert> oder …/app_full_proxy.php?…&src=<url_codiert>. Diese muss man rausfilter, die tatsächliche URL dekodieren:

3. Profilbilder von altersbeschränkten Seiten sind ebenfalls altersbeschränkt 🙂 Für Zugriff muss man den Access Token anhängen (Quelle finde ich gerade nicht mehr wieder):

ggf. benötigt man das auch für die unter 1. genannten Bilder.

HTH

CodeIgniter: “The model name you are loading is the name of a resource that is already being used”

Es gibt im Internetz verschiedene Ansätze für dieses Problem; die meisten basieren auf einem eigenen Loader. Meiner Erfahrung anch funktionieren die alle nicht (in allen Situationen), und leider scheint die einzige echte Alternative zu sein, alle Models ohne Namen zu laden. Also lieber

statt

🙁

MySQL vs. UTF-8 (vs. CodeIgniter)

MySQL kann UTF-8… falsch. Besser gesagt: Ungenau. MySQL kann UTF-8, aber das heißt da anders:

Ein Zeichen in UTF-8 ist (bis zu) vier Bytes lang, “UTF-8” in MySQL kann aber nur bis zu drei. Deswegen fällt der Unterschied oft gar nicht auf, oder erst beim ersten Zeichen, das vier Bytes braucht. Will man echtes UTF-8, muss man einiges umkonfigurieren; in Kurzform:

  1. SQL muss in der Version 5.3.3+ vorhanden sein, siehe Link oben
  2. Jede Datenbank, jede Tabelle (ggf. jede Spalte) müssen auf “CHARSET=utf8mb4 COLLATE utf8mb4_unicode_ci” umgestellt werden
    • Dabei zu beachten: InnoDB kann wohl nur 767 Bytes pro Index Schlüssel. Wenn man vier (statt drei) Bytes pro Zeichen rechnet, sind das bsplw. nur 191 (statt 255) Zeichen!
    • Das gilt auch für Typen wie TINYTEXT, die wohl nur 255 Bytes = 63 Zeichen bei 4 Byte/Zeichen speichern können. Ggf. ist hier ein anderer Typ zu wählen
  3. ggf. Server, Client, etc. umstellen (dabei hilft “SHOW VARIABLES WHERE Variable_name LIKE 'character\_set\_%' OR Variable_name LIKE 'collation%';“)
  4. ggf. Datenbank reparieren und optimieren (war bei mir nicht nötig, ich habe alle Tabellen neu angelegt):

bzw.

Insbesondere bei Punkt 3 hängt es bei CodeIgniter, btw. Wenn man in der Datenbank-Config

 stehen hat, dann ist das ebenfalls nicht das echte UTF-8. Stattdessen korrekt ist:

Update

Das Problem betrifft auch mysqldump: Will man die eine utf8mb4-Datenbank dumpen, muss man das explizit sagen, denn mysqldump nutzt per Default nur utf8 (Quelle):

Das ist so tief verankert, dass auch Dumps über Tools wie Sequel Pro nur utf8 nutzen m(

Synology: Seinen eigenen DDNS-Service hosten

Achtung: Setzt inwx.de als Registrar voraus!

Der Service

Auf xuad.net gibt es eine entsprechende Anleitung für einen DDNS-Service, auf GitHub liegen die Sourcen. Dabei ist einiges zu beachten:

Man muss dieses Skript unter einer anderen (Sub-) Domain als das DDNS-“Ziel” laufen lassen, klar. Sobald man die DDNS-Domain dynamisch um-mapped, ist das Skript sonst nicht mehr erreichbar.

Wer bei INWX ein Passwort mit Sonderzeichen verwendet, sollte es in der .ini in Anführungszeichen setzen.

Wenn man die IP auslesen möchte, statt sie zu übergeben (siehe Comments in dem Post), und der Server nur schon IPv6 spricht, sollte man den hier beschriebenen “Fix” verwenden, denn INWX erwartet eine v4-Adresse:

Darüber hinaus muss bei INWX ein expliziter Eintrag für die Subdomain angelegt sein. Das Skript ruft diesen bestehenden Eintrag ab, extrahiert eine ID, und updated dann über diese ID. Gibt es keinen Eintrag, gibt es auch keine ID, und das Update kann nicht durchgeführt werden. Ein Wildcard-Eintrag genügt nicht.

Dann sollte das klappen. Falls nicht, kann man im DDNSManager.php Debugging einschalten, um Request und Response an INWX ausgeben zu lassen:

Und/oder PHP-Fehler anzeigen, klar:

 Den Service aufrufen (lassen)

Interessanter ist die Frage, wie und wann sich das NAS dann registriert. Der hauseigene DDNS-Client bietet zwar drölfzig Anbieter zur Auswahl, aber kein Feld für custom Skripte 🙁

Erste Idee: cron. Zwar bringen die Synologys cron mit, allerdings will man sich mit einem regelmäßigen Cronjob ja nicht seinen Ruhezustand versauen.

Zweite Idee: dhclient-exit-hooks.d (siehe auch), dhcpcdnetworkmanager, oder irgendwas, das einen Hook bei IP-Änderung bietet. Gibt es auf einem Synology offenbar (so) nicht.

Dritte Idee: Den erwähnten DDNS-Dienst pimpen. Google liefert tatsächlich einen Ansatz:

  • per SSH auf dem NAS einloggen, Benutzer “root”, Passwort vom “admin”-Account
  • /etc.defaults/ddns_provider.conf editieren (bsplw. mit vi)
  • Einen neuen Eintrag anlegen a la

Hier wäre übrigens möglich, die IP zu übergeben, dann muss man sie nicht auslesen: Neben __USERNAME__ und __PASSWORD__ sind __HOSTNAME__ und __MYIP__ möglich. Trotzdem: Die IPv6-Übersetzung scheint angebracht, wie gesagt wegen INWX.

htaccess-Authentifizierung habe ich hier nicht hinbekommen, sobald sich das ändert, gibt es hier ein Update.

Nun kann man im DSM seinen neuen DDNS-Service auswählen; “Hostname” kann in diesem Fall mit irgendwas gefüllt werden, da er in der Query-URL ja nicht verwendet wird

HTH