tobias.bloggt

von weiten reisen, nerdigen skripten und kreativem agenturleben

Archiv für die Kategorie ‘technikkram




E-Mails mit PHP zu ver­sen­den geht ja ansich ganz ein­fach und kom­for­ta­bel. In­ter­es­sant wird es je­doch, wenn Text­mails mit ma­nu­el­len Zei­len­um­brü­chen ver­schickt wer­den sollen.

Ein Zei­len­um­bruch wird im Mail­text bei­spiels­weise wie folgt erzeugt:

$mailtext = "Ich bin ein Absatz.\n";
$mailtext.= "Und ich auch!";

Da­mit die Steu­er­zei­chen \n, \r oder \t in­ter­pre­tiert wer­den müs­sen sie in dop­pel­ten An­füh­rungs­zei­chen ste­hen! Ein­fa­che An­füh­rungs­zei­chen im obi­gen Bei­spiel wür­den be­wir­ken, dass das ab­schlie­ßende \n im E-Mail-Text aus­ge­ge­ben wird.

Soll nun Text aus ei­ner MySQL-Datenbank in die E-Mail flie­ßen, und wurde die­ser Text zu­vor über ein HTML-<textarea>–Feld in der Daten­bank ge­spei­chert, wer­den plötz­lich alle Daten­bank­in­halte mit dop­pel­ten Zei­len­um­brü­chen in der E-Mail dargestellt.

Grund da­für ist die Form des Zei­len­um­bruchs der beim Spei­chern in die Daten­bank ge­schrie­ben wird. Und das ist bei der Überg­abe des HTML-<textarea>–Fel­des ein so­ge­nann­ter „Windows-Zeilenumbruch“ im For­mat \r\n. Nor­ma­ler­weise in­ter­pre­tie­ren die meis­ten Pro­gramme diese Umbruchs-Kombination (Carriage-Return + New­Line) als ei­nen zu­sam­men­hän­gen­den Um­bruch, die gän­gi­gen E-Mail-Clients hin­ge­gen in­ter­pre­tie­ren \r\n als dop­pel­ten Zei­len­um­bruch. Auch un­ter Windows.

Um diese Miss­in­ter­pre­ta­tion zu um­ge­hen sollte vor Ver­sand der E-Mail der kom­plette E-Mail-Text nach die­ser Zei­chen­kom­bi­na­tion durch­sucht wer­den. Am ein­fachs­ten geht dies mit ei­nem Re­gu­lä­ren Aus­druck der alle Vor­kom­nisse von \r\n durch \n ersetzt.

$mailtext = "Ich bin ein Absatz.\n";
$mailtext.= "Und ich auch! Nach mir kommt Datenbankinhalt.";
$mailtext.= $datenbankinhalt;
 
$mailtext = preg_replace("%\r\n%", "\n", $mailtext);

Vor ei­ni­gen Mo­na­ten habe ich eine Lö­sung zum Upload von docx– und xslx-Dateien in Mediawiki-Wiki’s gepostet.

In­zwi­schen ist das E-Book in al­ler Munde, und na­tür­lich soll man auch frei ver­füg­bare E-Books im Wiki hoch­la­den dür­fen. Doch auch hier wird der Mi­me­Type wie­der nicht rich­tig erkannt.

file -bi un­ter Li­nux lie­fert für ein E-Book im po­pu­lä­ren „EPUB“-Format den Mi­me­Type application/x-zip, PHP in­ter­pre­tiert je­doch als application/zip. Ganz rich­tig wäre übri­gens application/epub+zip.

Um den EPUB-Upload zu ge­stat­ten muss also die Ex­ten­sion epub dem Ar­ray $wgFileExtensions in der LocalConfig.php hin­zu­ge­fügt wer­den. Da­nach muss noch die Da­tei mime.types im Ord­ner /includes an­ge­passt wer­den. Dazu muss le­dig­lich die Zeile application/zip [...] um den Ein­trag epub er­wei­tert werden.


Wer viel mit CSS ar­bei­tet und („krasse“) ta­bel­len­freie Web-Layouts er­stellt, der wird nicht drum­herum kom­men, sich mit den CSS-Selektoren zu be­schäf­ti­gen.
In­zwi­schen bin ich et­was XPATH–ver­wöhnt was Se­lek­to­ren an­geht, denn mit self, child, parent, descendant, descendant-or-self, ancestor, ancestor-or-self, preceding, following, preceding-sibling und following-sibling hat man dort alle, und da­mit meine ich wirk­lich ALLE, Mög­lich­kei­ten im XML-(Struktur-)Baum zu na­vi­gie­ren und Ele­mente zu selektieren.

Die Mög­lich­kei­ten in CSS sind da­ge­gen eher be­schei­den. Hier eine kurze Über­sicht über die wich­tigs­ten CSS-Selektoren:

  • klasse klasse2
    Se­lek­tiert klasse2 die sich *ir­gendwo* in­ner­halb von klasse be­fin­det.
    Beispiel:

    <div class="klasse">
      <span class="klasse2">Dieser SPAN wird selektiert.
        <p class="klasse2">Dieser Absatz wird selektiert.</p>
      </span>
    </div>
  • klasse>klasse2
    Se­lek­tiert klasse2, wenn sich diese ge­nau EINE Ebene un­ter­halb von klasse be­fin­det.
    Beispiel:

    <div class="klasse">
      <span class="klasse2">Dieser SPAN wird selektiert!
        <p class="klasse2">Dieser Absatz wird NICHT selektiert.</p>
      </span>
    </div>
  • klasse*klasse2
    Se­lek­tiert klasse2, wenn sich diese ge­nau ZWEI Ebe­nen un­ter­halb von klasse be­fin­det.
    Beispiel:

    <div class="klasse">
      <span class="klasse2">Dieser SPAN wird NICHT selektiert.
        <p class="klasse2">Dieser Absatz wird selektiert.</p>
      </span>
    </div>
  • klasse+klasse2
    Se­lek­tiert klasse2, wenn sich diese di­rekt NACH klasse be­fin­det.
    Beispiel:

    <div class="klasse">
      <span class="klasse2">Dieser SPAN wird NICHT selektiert.</span>
    </div>
    <div class="klasse2">
      Dafür wird dieses DIV selektiert.
    </div>

Was ich mir für CSS wirk­lich wün­sche, ist ein parent-Selektor. Der würde viel Ar­beit ab­neh­men! Ist aber in CSS3 lei­der nicht vor­ge­se­hen und bis CSS4 raus­kommt könn­ten noch ein paar Jähr­chen ver­ge­hen. Schade…

CSS 4 You bie­tet auf zwei Sei­ten noch wei­tere In­fos zum Um­gang mit Selektoren.


Wir neh­men fol­gen­den Fall an:

Es soll eine per­ma­nente Wei­ter­lei­tung er­fol­gen von der al­ten URL http://www.domain.de/wiki/ nach http://wiki.domain.de/. Alle an­ge­for­der­ten Sei­ten und Ver­zeich­nisse (bei­spiels­weise index.php?title=Hauptseite) sol­len eben­falls wei­ter­ge­lei­tet werden.

Am ef­fek­tivs­ten wäre hier eine Wei­ter­lei­tung mit­tels mod_rewrite, für An­fän­ger je­doch nicht ohne wei­te­res zu im­ple­men­tie­ren, und auch nur dann mög­lich, wenn der Web­ser­ver, sprich Apa­che dies erlaubt.

Eine Al­ter­na­tive dazu stellt das fol­gende kleine PHP-Script dar:

<?php
/**
 * Permanente Weiterleitung
 * @author Tobias Fischer / tobias (at) mediaversal (punkt) de
 * @date 2009-04-24
 */
 
$url = $PHP_SELF . '?' . $QUERY_STRING;
$url = str_replace('/wiki', '', $url);
 
header("Status: 301 Moved Permanently");
header("Location: http://wiki.domain.de" . $url);
 
exit;
?>

$PHP_SELF gibt das Ver­zeich­nis und die auf­ge­ru­fene Da­tei ab Do­mai­nebene zu­rück, in un­se­rem Fall also /wiki/index.php. $QUERY_STRING lie­fert alle an­ge­häng­ten Va­ria­blen „nach dem Fra­ge­zei­chen“.
Nun müs­sen wir diese bei­den Frag­mente nur noch zu­sam­men­set­zen, dar­aus das ak­tu­elle Ar­beits­ver­zeich­nis /wiki ent­fer­nen und an die neue URL http://wiki.domain.de/ anhängen.

Die Sta­tus­mel­dung 301 teilt dem Brow­ser oder auch even­tu­el­len Such­ma­schi­nen mit, dass die an­ge­for­derte Seite per­ma­nent um­ge­zo­gen ist – 302 da­ge­gen würde tem­po­rär umleiten.


Fol­gen­des Sze­na­rio: Ein Daten­bank­ta­belle ent­hält alle Bank­leit­zah­len Deutsch­lands in­klu­sive der Kurz­be­zeich­nung der Bank. 20.107 Daten­sätze (Stand 04/2009). Al­ler­dings ent­hält die Daten­bank viele dop­pelte Daten­sätze die ge­löscht wer­den sol­len um Platz zu sparen.

CREATE TABLE IF NOT EXISTS `bankcode` (
`ID` SMALLINT(5) UNSIGNED NOT NULL AUTO_INCREMENT,
`Bankleitzahl` VARCHAR(8) NOT NULL,
`Kurzbezeichnung` VARCHAR(50) NOT NULL,
PRIMARY KEY  (`ID`),
KEY `Bankleitzahl` (`Bankleitzahl`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='Bankleitzahlen' AUTO_INCREMENT=20108;

Code: SQL-Tabellenstruktur

Eine reine SQL-Möglichkeit, Du­blet­ten zu fin­den und zu lö­schen habe ich nicht her­aus­ge­fun­den, des­halb habe ich mir ein kur­zes php-Script dazu ge­schrie­ben.
An­mer­kung: Bei 20.000 Daten­bank­ein­trä­gen musste ich dazu je­doch die max_execution_time in der php.ini von 60 (Se­kun­den) auf 320 er­hö­hen. Ob dies bei ei­nem Pro­duk­tiv­sys­tem sinn­voll ist, sei da­hin­ge­stellt, das Script lief bei mir lo­kal auf dem Rech­ner und ich habe die Ände­rung so­mit für gut hei­ßen können.

<?php
/**
 * Dubletten finden und löschen
 * @author Tobias Fischer / tobias (at) mediaversal (punkt) de
 * @date 2009-04-12
 */
 
// Datenbankverbindung aufbauen
/*
...
*/
 
// Anzahl der Datensätze auslesen und ausgeben
$result = mysql_query("SELECT `ID` FROM `bankcode`;");
$datensaetze = mysql_num_rows($result);
echo 'Datensätze: '.$anzahl.'<br /><br />';
 
// Arrays erstellen die in den for-Schleifen verwendet werden
$dubletten_ids = array();  //zählt die Dubletten eines Datensatzes
$overall_ids = array();  // zählt die Gesamtanzahl der Dubletten
 
// Erste Schleife - durchläuft jeden der 20.000 Datensätze einzeln
for($i=0;$i< $datensaetze;$i++) {
  if(!in_array($i, $overall_ids)) {
    $fid = mysql_result($result,$i,0);
    $result2 = mysql_query("SELECT * FROM `bankcode` WHERE `ID` = " . $fid . ";");
 
    for($k=0;$k<mysql_num_rows($result2);$k++) {
      $bcid =  mysql_result($result2,$k,0);
      $blz =  mysql_result($result2,$k,1);
      $kurz =  mysql_result($result2,$k,2);
 
      // Sucht nach Datensätzen mit gleicher BLZ und gleicher Kurzbezeichnung
      // AND `ID` <> '".$bcid."' schließt den aktuellen Datensatz aus
      $result3 = mysql_query("SELECT `ID` FROM `bankcode` WHERE `Bankleitzahl` = '" . $blz . "' AND `Kurzbezeichnung` = '" . $kurz . "' AND `ID` <> '" . $bcid . "';");
 
      // Mache weiter falls Dubletten vorhanden sind
      if(mysql_num_rows($result3) != 0) {
 
        // Durchläuft die Ergebnismenge und speichert alle ID's der gefundenen Dubletten 
        for($m=0;$m<mysql_num_rows ($result3);$m++) {
          $dubletten_ids[$bcid][$m] = mysql_result($result3,$m,0);
        }
 
        echo '<br />';
        echo 'BLZ "'.$blz.'" und Kurzbez. "'.$kurz.'" (ID: '.$bcid.') kommen unter folgenden IDs nochmals vor:';
        echo '<br />&nbsp;&nbsp;';
 
        // Aktuelles Dubletten-ID-Array nochmals durchlaufen und 
        // a) ID's ausgeben
        // b) jede ID im Array $overall_ids speichern
        //     dies hat den Zweck, dass Datensätze mit dieser ID in der ersten Schleife übersprungen werden
        //     da sonst eine Dopplung vorliegen würde
        while(list($key,$value) = each($dubletten_ids[$bcid])) {
          $overall_ids[] = $value;
 
          echo '<span ';
 
          // Um die Dubletten sofort zu löschen, folgende Zeilen einkommentieren
          /*
            $delete = mysql_query("DELETE FROM `bankcode` WHERE `ID` = " . $value . " LIMIT 1;");
            if($delete) {
              echo ' style="color:green;"';
            } else {
              echo ' style="color:red;"';
            }
          */
 
          echo '>';
          echo $value;
          echo ',</span> ';
        }
        echo '<br />';
 
        // Frühzeitige Übergabe an den Browser
        flush();
      }
    }
  }
}
 
echo '<br />';
// Gesamtanzahl aller Dubletten
echo count($overall_ids);
 
?>

Code: PHP-Code-Snippet

Ich konnte da­mit ca. 12.000 Du­blet­ten aus der BLZ-Tabelle lö­schen und die Daten­menge so­mit um mehr als 50 % reduzieren.

Für den Ein­satz in ei­nem an­de­ren Daten­bank– und Pro­jekt­um­feld müs­sen die Daten­bank­ab­fra­gen, die HTML-Textausgaben und even­tu­ell auch ei­nige Va­ria­blen an­ge­passt bzw. um­be­nannt werden!

Ich über­nehme keine Ge­währ für die Rich­tig­keit des Skripts und hafte so­mit auch nicht für even­tu­ell ent­stan­dene Schä­den durch fal­schen Ein­satz auf Dritt– oder Produktivsystemen!




    Kanada-Urlaub

    Was bis­her ge­schah: Van­cou­ver, Van­cou­ver Is­land, Vic­to­ria, To­fino, Van­cou­ver, Har­ri­son Hot Springs, Fra­ser Can­yon, Hell’s Gate, Kam­loops, Cle­ar­wa­ter, Blue Ri­ver, Jas­per, Lake Louise, Banff, Cal­gary, Ed­mon­ton, Kam­loops, Adams Lake, Lyt­ton, Burnaby, Vancouver

    Statusleiste

    tobias.twittert



tobias.bloggt

    Hej hej, jag heter Tobias och är tjugofyra år gammal. Das, liebe Freunde der Sprachenkultur, war Schwedisch. Und nochmals auf deutsch: mein Name ist Tobias und ich bin 24 Jahre alt. An der Hochschule der Medien in Stuttgart habe ich acht Semester "Druck- und Medien­tech­no­logie" studiert. Anfang 2007 habe ich mich mit [mediaversal] selbstständig gemacht, meinem StartUp das mir die Zeit des Studiums etwas versüßte. Seit April 2011 arbeite ich jedoch festangestellt bei der pagina GmbH und entwickle und gestalte E-Books. Und was sonst noch so los ist erfahrt ihr hier im Blog!