Die klingen ganz interessant, und der dargstellte Sampler klingt auch vielversprechend… andererseits: Meine jährliche Scherben-Phase hält auch nie lange an.
Photoshop: Transparente Bereiche in PNGs wiederherstellen
PNGs können einige Informationen mehr enthalten, als nur die sichtbaren Pixel. Zum Beispiel Photoshop-Hilfslinien. Oder aber die ursprünglichen Pixel in den transparenten Bereichen 🙂 ImageMagick kann diese Bereiche mittels
|
1 |
convert transparent.png -alpha off restored.png |
wiederherstellen (Danke, Björn). Photoshop kann das natürlich auch, und dafür wird auch kein Plugin (wie zB Remove Transparency) benötigt: Was man aus Sicht von Photoshop machen möchte, ist eine Konvertierung der transparenten Bereiche in eine durch Photoshop direkt manipulierbare (=löschbare) Form: Menü “Ebene” -> Ebenenmaske -> Aus Transparenz (Quelle). Die erzeugte Ebenenmaske kann man löschen oder mittels Shift+Click ausblenden, et voilà!
Also: Wenn das nächte mal PNGs mit aus Geheimhaltungsgründen “gelöschten” Bereichen bei euch auf dem Schreibtisch landen… 😉 Das alles geht natürlich nur, wenn das PNG nicht dahingehend “optimiert” wurde, dass abmaskierte Pixel gelöscht wurden.
Nicht’s ist schwieriger als der Deppenapostroph

Gesehen in der Kantine der Finanzbehörde Hamburg
10651

Notiz an mich selber: Wenn man sich im Falle eines kritischen Fehlers eine Mail schicken lässt, dann sollte man dafür sorgen, dass das nur 1x passiert – nicht mehrfach pro Minute das ganze Wochenende durch 🙂
Zu meiner Verteidigung: Es ging dabei um das eigentlich planmäßige Ablaufen eines Facebook-Tokens. Damit hatte ich gerechnet, und mir einen Kalendereintrag gemacht, wodurch der Fehlerfall nicht aufgetreten wäre. Aber offenbar läuft der Token früher ab, als angegeben m(
Kommentare, die die Welt nicht braucht – XLIV

via Robs
Der Hobbit (1985)
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:
|
1 |
$GLOBALS['TL_HOOKS']['postUpload'][] = array('ZipUploadCustom', 'handleFiles'); |
Sowie die Klasse als solche (direkt im Modulordner):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// extend System to have access to the logger: class ZipUploadCustom extends System { public function handleFiles($arrFiles) { foreach ($arrFiles as $file) { $file = TL_ROOT . '/' . $file; if(substr(strtolower($file), -4) === '.zip') { $this->extractZIP($file); } } } // ... } |
Das war’s im Wesentlichen. Der Vollständigkeit halber hier noch die Funkion zum Entpacken, sowie ein wenig Aufräumen der hochgeladenen Dateien:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
private function extractZIP($file) { $zip = new ZipArchive(); if ($zip->open($file) === TRUE) { $dir = dirname($file); // extract stuff: if($zip->extractTo($dir)) { // clean up: $this->cleanUpSystemFiles($dir); unlink($file); } $zip->close(); $this->log("ZIP \"$file\" successfully extracted", 'ZipUploadCustom extractZIP()', TL_FILES ); } else { $this->log("cannot open ZIP \"$file\"", 'ZipUploadCustom extractZIP()', TL_ERROR); } } private function cleanUpSystemFiles($dirPath) { $this->log("clean up $dirPath", 'ZipUploadCustom cleanUpSystemFiles()', TL_FILES); foreach (new DirectoryIterator($dirPath) as $fileInfo) { if($fileInfo->isDot()) continue; $fileName = $fileInfo->getFilename(); //handle directories: if($fileInfo->isDir()) switch (TRUE) { // unwanted directories, tbc: case $fileName == '__MACOSX': case strpos($fileName, '.') === 0: $this->log("deleting directory $fileName", 'ZipUploadCustom cleanUpSystemFiles()', TL_FILES); $this->deleteDirectory($fileInfo->getPathname()); break; } // handle files: else switch (TRUE) { // unwanted files, tbc: case strpos($fileName, '.') === 0: $this->log("unlinking $fileName", 'ZipUploadCustom cleanUpSystemFiles()', TL_FILES); unlink($fileInfo->getPathname()); break; } } } // http://stackoverflow.com/a/15111679 private function deleteDirectory($dirPath) { foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dirPath, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST) as $path) { $path->isDir() ? rmdir($path->getPathname()) : unlink($path->getPathname()); } rmdir($dirPath); } |
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:
|
1 |
array_insert($GLOBALS['TL_CTE']['includes'], 0, array('iFrame' => 'IframeCustom')); |
In /config/database.sql fügt man die gewünschten Datenbank-Felder hinzu:
|
1 2 3 4 5 |
CREATE TABLE `tl_content` ( `iframe_width` varchar(255) NOT NULL default '', `iframe_height` varchar(255) NOT NULL default '', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; |
In /dca/tl_content.php ergänzt man die neuen Felder und deklariert die Palette:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// Ergänzen der Palette um iFrames: $GLOBALS['TL_DCA']['tl_content']['palettes']['iFrame'] = 'type,singleSRC,iframe_width,iframe_height;{protected_legend:hide},protected;{expert_legend:hide},guests,cssID,space'; // Hinzufügen der Feld-Konfiguration für iFrames $GLOBALS['TL_DCA']['tl_content']['fields'] = array_merge($GLOBALS['TL_DCA']['tl_content']['fields'], array ( // Source selector; Auswahl aus der Dateiverwaltung; erlaubt nur Dateien, keine Ordner: 'singleSRC' => array ( 'label' => &$GLOBALS['TL_LANG']['tl_content']['singleSRC'], 'exclude' => true, 'inputType' => 'fileTree', 'eval' => array('fieldType'=>'radio', 'files'=>true, 'filesOnly'=>true, 'mandatory'=>true) ), 'iframe_width' => array ( 'label' => &$GLOBALS['TL_LANG']['tl_content']['iframe_width'], 'exclude' => true, 'inputType' => 'inputUnit', 'options' => array('px', '%'), 'eval' => array('mandatory'=>true,'rgxp'=>'alnum', 'tl_class'=>'w50') ), 'iframe_height' => array ( 'label' => &$GLOBALS['TL_LANG']['tl_content']['iframe_height'], 'exclude' => true, 'inputType' => 'inputUnit', 'options' => array('px', '%'), 'eval' => array('mandatory'=>true,'rgxp'=>'alnum', 'tl_class'=>'w50') ), )); |
In languages/de/tl_content.php und analog in languages/en/tl_content.php ergänzt man die referenzierten Texte:
|
1 2 3 |
$GLOBALS['TL_LANG']['CTE']['iFrame'] = array('iFrame', 'Erzeugt das HTML Element iFrame.'); $GLOBALS['TL_LANG']['tl_content']['iframe_width'] = array('Breite', 'Die Breite des iFrame. Attr.: width'); $GLOBALS['TL_LANG']['tl_content']['iframe_height'] = array('Höhe', 'Die Höhe des iFrame. Attr.: height'); |
Nun kommt die neue Klasse IframeCustom:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class IframeCustom extends ContentElement { protected $strTemplate = 'ce_iframe_custom'; protected function compile() { $this->Template->src = $this->singleSRC; $this->Template->width = unserialize($this->iframe_width); $this->Template->height = unserialize($this->iframe_height); } public function generate() { if(!strlen($this->singleSRC)) { return '<div>[no file selected]</div>'; } if(!is_file(TL_ROOT . '/' . $this->singleSRC)) { return '<div>[file not found]</div>'; } return parent::generate(); } } |
Und schließlich das Template ce_iframe_custom.tpl:
|
1 2 3 4 |
<div class="<?php echo $this->class; ?> block"<?php echo $this->cssID; ?><?php if ($this->style): ?> style="<?php echo $this->style; ?>"<?php endif; ?>> <iframe src="<?php echo $this->src ?>" width="<?php echo $this->width['value'].$this->width['unit'] ?>" height="<?php echo $this->height['value'].$this->height['unit'] ?>" title="iframe" scrolling="no"> </iframe> </div> |
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
- de/
- templates/
- ce_image_custom.tpl
- ContentImageCustom.php
- config/
Im ersten Schritt hängt man sich in das CMS:
In config/config.php registriert man eine eigene Klasse für den Typ “Image”:
|
1 |
$GLOBALS['TL_CTE']['images']['image'] = 'ContentImageCustom'; |
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:
|
1 2 3 4 |
CREATE TABLE `tl_content` ( `is_retina` char(1) NOT NULL default '', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; |
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.
|
1 2 3 4 5 6 7 8 9 10 11 |
// Anpassung der Palette für is_retina $GLOBALS['TL_DCA']['tl_content']['palettes']['image'] = str_replace('caption', 'caption,is_retina', $GLOBALS['TL_DCA']['tl_content']['palettes']['image']); // Hinzufügen der Feld-Konfiguration für is_retina $GLOBALS['TL_DCA']['tl_content']['fields']['is_retina'] = array ( 'label' => &$GLOBALS['TL_LANG']['tl_content']['is_retina'], 'exclude' => true, 'inputType' => 'checkbox', 'eval' => array('mandatory'=>false, 'tl_class'=>'w50 m12') ); |
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:
|
1 |
$GLOBALS['TL_LANG']['tl_content']['is_retina'] = array('ist ein Retinabild', 'Bitte legen Sie fest, ob es sich um ein Retinabild handelt.'); |
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:
|
1 2 3 4 |
class ContentImageCustom extends ContentImage { protected $strTemplate = 'ce_image_custom'; } |
Das genannte Template ist templates/ce_image_custom.tpl, und darin sieht’s dann so aus:
|
1 |
<?php echo $this->is_retina ? 'retina' : 'not retina'; ?> |
(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