Friendwatch: Web 2.0 und lokalisierte Dienste

Die Macher von Friendwatch haben schon sehr erfolgreich mehrere Communities aufgebaut (bspw. Gothic-Singles, Gothic-Vote, …). Die Idee ist so was von Web 2.0-mässig: Man trägt verschiedene Orte ein, die im Leben eine Rolle spielen: Wohnort, Arbeitsstätte, … und kann auf einer Karte sehen wer sich ab und zu in der Nähe aufhält. Das Design ist schön, die Karten schnell, alles ist voller XHTTP-Requests und es funzt schon ziemlich gut, obwohl es noch den Betastatus hat. Die Idee, das globale Netz mit den lokalen, tatsächlichen Gegebenheiten zu verbinden ist gut, denn richtige Treffen sind durch rein virtuelle Communities halt immernoch nicht zu ersetzen (vielleicht meine ich das auch nur, weil ich so alt bin 🙂 )…

Nun beginnt das Huhn-Ei Problem: Es braucht viele Eintragungen bis es läuft und erst wenn es läuft gibt es viele Eintragungen…

Emailadressen auf Webseiten codieren

Update 01.2011: Tillate hat einen super Artikel dazu.
Spammer brauchen Emailadressen. Neue Emailadressen bekommen sie, indem sie (wie Google) mit sog. Robots von Website zu Website springen, den Inhalt herunterladen und die Emailadressen herauspfriemeln. Das heisst, man sollte seine Emailadressen im WWW verstecken, so gut dass es geht. Eine Möglichkeit ist die Benutzung von JavaScript (weil das die Addressharvester anscheinend noch nicht so beherrschen), eine Andere ist die Verwendung von Bildern (es gibt natürlich noch mehr).

Ich habe dazu ein kleines PHP Skriptchen geschrieben. Es kann eine Emailadresse alleine oder viele Emailaddressen in Text eingebettet verstecken. Im Zip hat es 3 Dateien:

  • hidemail.php: Das Hauptscript, das die Arbeit erledigt.
  • imagemke.php: Ein Script, das aus den Mailadressen Bilder erzeugt wenn das Javascript ausgeschaltet ist.
  • hmexample.php: Ein Beispiel, das die Anwendung zeigt. Es gibt auch eine online Demo.

Ich würde mich über Feedback freuen :)…. Download: hidemail PHP Skript um Mails zu hiden. Zum Gebrauch müssen einige Einstellungen (beispielsweise die URL zu imagemke.php in hidemail.php gemacht werden.

Ideen sind von:


Nachtrag Juli 2008:
Einen Interessanten Beitrag dazu gibt es beim Tillate Techblog!

Die Spamfalle für Webmaster

Der eine Weg, gegen Spammer vorzugehen, ist ihnen die Email-Adresse nicht zu geben. Der Andere Weg wäre es, ihnen gaaaaanz viele Adressen zu geben! Die Ursprüngliche Idee ist von Neil Gunton, hier etwas praktisch angepasst.

Die Grundidee ist folgende: Wir verbieten das Indizieren bestimmter Seiten. Liebe Roboter halten sich dran, böse nicht. Auf diesen Seiten Bieten wir ganz viele Emailadressen und weitere Links auf andere Seiten an mit noch mehr Adressen. Ich hoffe dann mal, dass sich die Datenbank des Spammers füllt, mit falschen Mailadressen…

Installation:

Das PHP-Skript kreiert verschiedene Seiten mit Fiesitäten… Weitergehende Links im Kreis, Links auf andere Spamtraps, defekte und korrekte Emailadressen, etcetc… Es sollte in ein eigenes Verzeichnis (bspw: /mails/) kopiert werden. Man kann auch ein laufendes Beispiel ansehen. Bitte bedenke: Die Seiten müssen nicht schön sein, sondern Maschinenlesbar :)…

Um die netten Roboter abzuhalten im Kreis zu indizieren, verbieten wir ihnen das ganz einfach in der Datei /robots.txt:

# robots.txt
User-agent:  *
Disallow: /mails/

Damit die Spammer das auch finden, sollte auf möglichst vielen Seiten ein versteckter Verweis angebracht werden. Zum Beispiel mit einem durchsichtigen gif, 1×1 Punkt gross:

<a href="/mails/"><img alt="" src="/images/transparent.gif" width="1" height="1" border="0"/></a>

Denkbar wäre es auch, das Ganze mit einem nofollow Link zu versehen.

Nun sollen sie sich zu Tode fressen… Hier die Dateien: Spamtrap: Skript, robots.txt und transparent.gif.

Ebenfalls eine nette Lösung ist die Spidertrap. Sie blockiert die IPs der unanständigen Roboter und schliesst sie so aus.

Excel und Powerpoint mit htdig indizieren

Möchte man xls und ppt in den Index der Intranet Suchengine htdig aufnehmen, muss man etwas Magie wirken lassen:

  1. Es braucht ein Umwandler „gewünscht2txt“. Ich habe xls2csv für Excel und catppt für Powerpoint gewählt.
  2. Die Mimetypes sollten aktualisiert werden. /etc/htdig/mime.types:
    [...]
    application/vnd.ms-excel        xls
    application/vnd.ms-powerpoint   ppt
    [...]
  3. Externe Parser müssen aktiviert werden in /etc/htdig/htdig.conf:
    [...]
    external_parsers: application/msword /usr/share/htdig/parse_doc.pl \
    application/postscript /usr/share/htdig/parse_doc.pl \
    application/pdf /usr/share/htdig/parse_doc.pl \
    application/vnd.ms-powerpoint /usr/share/htdig/parse_doc.pl \
    application/vnd.ms-excel /usr/share/htdig/parse_doc.pl
    [...]
  4. Nun muss noch /usr/share/htdig/parse_doc.pl angepasst werden:
    [...]
    #
    # Excel
    #
    $XLS2CSV = "/usr/bin/xls2csv";#
    # Powerpoint
    #
    $CATPPT = "/usr/bin/catppt";
    [...]
    } elsif ( $ARGV[1] =~  /excel/) {       # it's MS Excel - this detection is a kludge
        $parser = $XLS2CSV;
        # convert all possible sheets to ascii
        $parsecmd = "$parser "$ARGV[0]" |";
        $type = "MS-Excel";
        $dehyphenate = 0;               # Excel documents not likely hyphenated
    } elsif ( $ARGV[1] =~  /powerpoint/) {
        $parser = $CATPPT;
        # convert all possible sheets to ascii
        $parsecmd = "$parser "$ARGV[0]" |";
        $type = "MS-Powerpoint";
        $dehyphenate = 0;
    }
    [...]
  5. Nun sollts funzen.

Man könnte es wohl auch über html machen (mit dem Helfer xlhtml). Dazu müsste die Zeile in htdig.conf heissen:

application/vnd.ms-excel->text/html /usr/share/htdig/parse_doc.pl

und dann müsste inparse_doc.pl xlhtml mit den korrekten Parametern aufgerufen werden.

Generell können folgende Probleme auftreten:

Apache Index:
Indiziert man Verzeichnisse, die das Apache Index Modul erzeugt (Dateibaum ohne index.html), so können Seiten mehrfach gespeichert sein weil sich die Parameter unterscheiden. Um dies zu verhindern muss htdig.conf ergänzt werden:

bad_querystr: ?C=D ?C=S ?C=M ?C=N ?O=A ?D=A ?D=D ?M=A ?M=D ?N=A ?N=D ?S=A ?S=D C=D C=S C=M C=N O=A D=A D=D M=A M=D N=A N=D S=A S=D

Ghostscript-Hanger:
Mein Indexer hatte die Tendenz beim Aufruf von gs zu hangen. Nundenn, ein Umstellen von „pstotext“ auf „pdftotext“ hat das ganze subjektiv schneller gemacht und bis jetzt ist es nimmer gestalled. In /usr/share/htdig/parse_doc.pl:

[...]
#
# set this to your PDF to text converter
#
#$CATPDF = "/usr/bin/pstotext";                         # From "pstotext"
$CATPDF = "/usr/bin/pdftotext";                         # From "pdftotext"
if (! -x $CATPDF) { $CATPDF = "/usr/bin/pdftotext"; }   # From "xpdf"/"xpdf-i"
if (! -x $CATPDF) { $CATPDF = "/usr/bin/ps2ascii"; }    # From a ghostscript
if (! -x $CATPDF) { $CATPDF = "/bin/true"; }
[...]

Ein Ajax Baum der sich sein Zustand merkt

Man gönnt sich ja sonst nichts… Ich wollte für eine mit dem Symfony Framework erstellte Webanwendung eine Baumansicht. Da leichtes implemenieren langweilig ist, musste es schon ein Ajaxbaum mit Kontextmenü und allem Schnickschnack sein. Nach längerem Suchen fiel die Wahl auf Dojo, einem Toolkit mit vielen Widgets und sonstigen Spassmachern. Der Baum besitz viele Nodes, darum sollten diese dynamisch nachgeladen werden (ok, etwas Masochismus war auch dabei). Die Anwendung besteht eigentlich aus herkömmlichen Seitenwechseln, und so musste der Baum auch seinen Zustand halten können. Nundenn, nach laaaaaanger Probierephase (die Dokumentation ist nicht gerade erschlagend) und der Hilfe vom Web wurde es dennoch Realität *freu*.

Hilfreiche Websites:

Tipps generell:

  • var djConfig = {isDebug: true }; aktiviert die Debugeingaben, dojo.debug(‚Blah‘); kann für Debugausgaben benutzt werden.
  • saveExpandedIndices und restoreExpandedIndices sind methoden des erweiterten tree-Controllers! Er muss also require’d und gemixint werden (siehe unten)
  • Der Baumzustand wird in einem Cookie gespeichert, damit hat man in der Applikation nichts mehr damit zu tun.

Wie das Ganze in Symfony integriert wurde kann gerne nachgefragt werden.

Nundenn, gimme Code:

<script type="text/javascript" src="/js/dojo.js"></script>
<script type="text/javascript">
var djConfig = {isDebug: true }; // Comment if debugguing
dojo.require("dojo.widget.Tree");
dojo.require("dojo.widget.TreeSelector");
dojo.require("dojo.widget.TreeNode");
dojo.require("dojo.widget.TreeContextMenu");
dojo.require("dojo.widget.TreeLoadingController");
dojo.require("dojo.widget.TreeControllerExtension");
// Do something if a node is clicked
function modulTreeSelectFired() {
    var treeSelector = dojo.widget.manager.getWidgetById('modulTreeSelector');
    var treeNode = treeSelector.selectedNode;

    < !get a reference to the songDisplay div –>
    //var hostDiv = document.getElementById("songDisplay");

    var isFolder = treeNode['isFolder'];
    if ( !isFolder) {
       //var song = treeNode['title']
       //hostDiv.innerHTML = "You clicked on "+song;
    } else {
       //hostDiv.innerHTHML = "";
    }
    //hostDiv.style.display = "";
}

// Set up Dojo and the tree
function init() {
    var treeSelector = dojo.widget.manager.getWidgetById('modulTreeSelector');
    dojo.event.connect(treeSelector,'select','modulTreeSelectFired');

    var modulTree = dojo.widget.manager.getWidgetById('modulTree');
    dojo.event.topic.subscribe(modulTree.eventNames.collapse, "saveExpandedIndices");
    dojo.event.topic.subscribe(modulTree.eventNames.expand, "saveExpandedIndices");

    // add extensions to controller
    dojo.lang.mixin(dojo.widget.byId('modulTreeController'), dojo.widget.TreeControllerExtension.prototype);

    // Restore old state
    restoreExpandedIndices();
 }

// Save the tree state
function saveExpandedIndices() {
        // You can save this object as tree persistent state
        indices = dojo.widget.byId('modulTreeController').saveExpandedIndices(
            dojo.widget.byId('modulTree')
        );
        var flatIndices = dojo.json.serialize(indices);
        //dojo.debug(flatIndices);
        dojo.io.cookie.setCookie('modulTree/saveindices',flatIndices, 365, null, null, null);
    }

// Restore the tree state
function restoreExpandedIndices() {
        flatIndices = dojo.io.cookie.getCookie('modulTree/saveindices');
        indices = dojo.json.evalJson(flatIndices);
        //dojo.debug(flatIndices)
        if(indices) {
            dojo.widget.byId('modulTreeController').restoreExpandedIndices(dojo.widget.byId('modulTree'), indices
            );
        }
    }

// Initialize
dojo.addOnLoad(init);</script>

Der HTML-Code sollte eigentlich klar sein: Controller, Kontextmenü und Tree aufbauen (nur die erste Hierarchiestufe) und der PHP-Teil ist nach Vorgabe. Sind mehr Details gewünscht, liefere ich diese gerne nach.

Stichworte: persistent tree state, remembering tree state

Ergänzung, Juli 2008: Oky, Ihr habt recht: Gimme Code! Schwatzen kann jeder!

Hier mal den Quellcode (eher das Gerüst, bzw proof-of-concept 🙂 ) des Moduls und den Inhalt des web/js Verzeichnisses. Hilft das was? Braucht es mehr, damit es sinnvoll ist?

Geschrieben wurde es mit Symfony 1.0 und Dojo 0.4 Rev: 6258.

In der Zwischenzeit bin ich aber von dem Dojo-Ajax Zeux wieder etwas weggekommen. Ich progge für Intra- und Extranetapplikationen, und da sind zu viele Browser mit zu vielen Konfigurationen im Einsatz. Für „öffentliche“ Websites ist es sicher schön, auch wenn es (wie bei Symfony üblich) nicht voll Ajax ist, sondern halt nur Komponentenweise, was ich nicht als schön empfinde…

Ich bin zurück zum guten alten PHP-Layersmenü (Beitrag dazu folgt noch). Das fallbackt sehr schön ohne Java Script.

Der HTML-Künstler

Blackant ist ein wahrer Code-Künstler. Unter vielen anderen wirklich genialen Projekten hat er ein Script (img2html), das den HTML-Code an Hand eines Bildes umformatiert. Für den normalen Anwender unsichtbar, entlockt es dem Experten stille Bewunderung.

Als Beispiel, die oncode.info Homepage als Stern formatiert: Hier umformatieren und danach den Quelltext ansehen.