2010-11-12

World Usability Day 2010 Hamburg (WUD2010HH)

Gestern war der World Usability Day 2010 - unter anderem auch in Hamburg.

In diesem Jahr fand die Konferenz nach dem BarCamp Prinzip statt. Da es nicht so viele Teilnehmer wie im letzten Jahr waren, vermute ich, dass dieses Konzept nicht so viele Leute anlockt - da man im Vorfeld nicht weiß über was geredet/diskutiert wird. Dennoch fand ich das Prinzip sehr gut, denn die Diskussionsrunden, die am Nachmittag stattfanden, waren sehr konstruktiv und informativ - Masse ist eben nicht Klasse.

Vielen Dank auch an das Orga-Team, das fleißig für den ganzen Aufbau und für Speis und Trank gesorgt hat.

Am Vormittag habe ich am Workshop "Living Place Hamburg: Neue Möglichkeiten zur benutzerzentrierten Entwicklung" teilgenommen. Workshop trifft es an dieser Stelle vielleicht nicht direkt, aber es war dennoch außerordentlich interessant: Kai von Luck hat die diversen Projekte der HAW Hamburg aus seinem Bereich vorgestellt:

  • Creative Lab: Projekte mit Künstlern
  • Development Lab: Business CSCW und Enterprise 2.0
  • Usability Lab: Usability Studien


Den Living Place Hamburg haben wir uns dann vor Ort angeschaut. Eine Ein-Zimmer-Wohnung als Usability Lab. Hier kann man untersuchen, wie sich Mensch und Technik im Ambiguous Computing verhalten.

Am Nachmittag habe ich mich an folgenden Diskussionen beteiligt:

  • Können theoretische Grundlagen aus der Psychologie für UX-Design dienen (und wie implementiert man dies)?
  • Agiles User Centered Design - Herausforderungen, Erfahrungen, Kompromisse, Lösungen.
  • Check-Ins: Wofür und warum.

Bilder von dem Event gibt es (bald?) bei flickr (WUD2010HH) und natürlich habe ich mich auch wieder am Micro-Sponsoring beteiligt.

In den folgenden Tagen werde ich meine Notizen zu den Sessions online stellen.


2010-11-05

Rubrik: Skurrile Fehlermeldungen zum Wochenende

Seit einiger Zeit arbeite ich mit einem Microsoft SQL Server 2008. Ich muss zugeben, dass ich noch nicht bis ins letzte Detail der Materie vorgedrungen bin, aber dennoch: Die Fehlermeldungen, die der Server so von sich gibt, sind alles andere als selbsterklärend.

Heutiges Beispiel:

Fehler im OLAP-Speichermodul: Ein doppelter Attributschlüssel wurde bei der Verarbeitung gefunden: Tabelle: dbo_dimVoucherCode, Spalte: Comment, Wert: . Das Attribut ist 'Comment'.

Nun gut, man kann sich da schon etwas drunter vorstellen: Das Attribut Comment enthält Werte, die nicht eindeutig sind. Das sehe ich ein. Verwunderlich ist lediglich, dass dieser Fehler aus dem Nichts auftaucht, aber wem glauben wir schon, wenn er sagt "Ich habe nichts geändert" ...

Was ich aber nicht verstehe, ist die Tatsache, das das Attribut an dieser Stelle auch gar nicht eindeutig sein muss - war es ja auch in der Vergangenheit nie!

A duplicate attribute key has been found when processing - ein Beitrag aus den Microsoft-Foren - geht auf diesen Fehler ein und Jeroen Schalken findet eine Lösung:
"In the end the solution was setting the TRIM to NONE on the key field. And leave the rest of the settings as set by AS2008. I can't think of an explenation."
Frei übersetzt so viel wie "Am Ende einer langen verzweifelten Reise, habe ich beim Key-Feld die Einstellung TRIM auf NONE gesetzt. [...] Ich kann mir nicht erklären, wieso das die Lösung ist."

Auch ich kann mir das nicht erklären, aber ich sehe, dass es funktioniert. Mit diesem ungelösten Geheimnis gehe ich jetzt ins Wochenende und vergesse die ganze Angelegenheit. Vielleicht stoße ich ja in ein paar Jahren wieder auf diesen Blog-Beitrag und belächle ihn, weil ich dann weiß, was da im Hintergrund des Servers so alles passiert. Bis dahin hilft er ja vielleicht auch dem ein oder anderen wie mir.

2010-11-01

OSX Bash Kommandos - meine Favoriten

Ein paar Highlights (wie ich finde):
  • pushd und popd: Ein Stack für Verzeichnisse
  • !! wiederholt das letzte Kommando. Sehr schön ist also:
    sudo !!
  • escape . macht das selbe ohne das Kommando gleich auszuführen
  • cp /path/longfilename.ext{,-old}

Ein paar Aliase:
  • alias mkcd='_(){ mkdir $1; cd $1; }; _' erstellt ein Verzeichnis und wechselt gleich hinein
  • alias ffile='find . -name $1': Suche nach Dateien
  • alias pwdc='pwd; pwd | pbcopy' gibt das aktuelle Verzeichnis aus und kopiert es gleich in die Zwischenablage


2010-10-27

Online-Marketing Notizen

In einem vorherigen Blogpost habe ich bereits die Online-Marketing Buzzwords aufgelistet, die mir bei der Lektüre von Einführungsliteratur zum Thema Online-Marketing über den Weg gelaufen sind.
Dieser Post erweitert nun das Themengebiet um ein paar Notizen, die ich mir beim Stöbern gemacht habe.
Ziel dieses Beitrages ist es nicht, das Thema umfangreich zu beleuchten. Dafür ist es selbstverständlich viel zu komplex, sondern lediglich ein paar relevante Stichpunkte aufzulisten.

Online-Marketing umfasst folgende Bereiche:
  • Affiliate-Marketing
  • Suchmaschinen-Marketing bestehend aus
    •  SEO
    •  AdWords
  • Online-Werbung
  • Email-Marketing
  • Sozial Networks

Stichpunkte zu den einzelnen Themen:
(BTW: Unterscheidung zwischen Webseite (einzelne Seite) und Website (gesamter Webauftritt) beachten.)

AdWords:
  • Keyword-Kombinationen mit Städtenamen sind billiger
  • wenige relevante Keywords reichen
  • Über welche Suchbegriffe landen Nutzer auf der Website?

Suchmaschinen-Optimierung:
  • Städtenamen in URL, Meta-Tags, etc. einfügen (auch aus Umland)

Onsite-Optimierungen: 
  • Textoptimierungen
  • Struktur der Website
  • valider HTML-Code
  • Writing for the Web
  • Tools zur Überprüfung der Keyword-Density
  • pro Webseite nur einem Thema widmen
  • Google Sitemap integrieren
  • inhaltliche Nähe verschiedener Seiten durch Verlinkungen untereinander unterstützen
  • relevante Suchworte in den title-Tag
  • URL und title-Tag koppeln
  • Linktext sollte Wörter enthalten, um die es auf der Zielseite geht

Offsite-Optimierung:
  • Wie bekomme ich Backlinks?
  • Platzierung in: Google News, Google Images, YouTube, MyVideo, Clipfish, Sevenload, Blogs, Google Maps
  • News: Presseportale nutzen, zB. openpr.de
  • Landingpages mit Video haben eine 20% höhere Conversionrate
  • YouTube Videos pushen durch viele Views und vielen Kommentare
  • Bildersuche: Suchbegriff häufig in Nähe des Bildes verwenden
  • Bilder mit mind. 400px Breite hochladen

Soziale Netzwerke:
  • Bis 2012 nutzen rund ein Drittel der deutschen Bevölkerung Soziale Netzwerke! (heise.de/newsticker/meldung/print/107311)

2010-10-24

"Append JavaScript at the end of my html file" - Component for Tapestry 5.1

This post describes a service, that enables Tapestry to append JavaScript files and script parts at the end of a html file. Script files and scripts can be "dropped into the service" from any page class or component and will be rendered in that order (FIFO) at the end of the page.

Upgrade Tapestry 5.0.x to Tapestry 5.1. loads included JavaScript files in the HTML head. Reasons are discussed in the Upgrade Notes: http://tapestry.apache.org/tapestry5.1/upgrade.html
project-wide release notes. They told me that in fix for bug TAP5-544:
Firstly, JavaScript libraries would have to move (back) to the head, permanently, no configuration [...].
Maybe I did not understand the real concern, but: Why isn't it up to the developer to decide where to place script files?
I decided to write a component that appends my script files at the end of my html files. The most important reason for this descicion is, that--following Yahoo's performance guidelines--I'd like to first load my HTML DOM before loading the script files.

Write a Service to Collect Files to Append
We need an Interface to be able to use the perthread scope (more about scoypes: "a per-thread instance is created on demand, behind a shared proxy").
The Interface just consists of 4 methods. 2 to add Script files and Scripts to the list and 2 to get the lists when they should be rendered:

public interface IAppendedScriptsService {

    /**
     * Add a script file to the list of script files to append.
     *
     * @param scriptFile
     */
    public void addScriptFile(String scriptFile);

    /**
     * Add a script to the list of scripts to append.
     *
     * @param script
     */
    public void addScript(String script);

    /**
     * Returns a list with script files to append. Needs to be a list to preserve order of script files.
     *
     * @return list with script files to append.
     */
    public List
getScriptFiles();

    /**
     * Returns a list with scripts to append. Needs to be a list to preserve order of scripts.
     *
     * @return list with scripts to append.
     */
    public List
getScripts();
}

The implementation of the Interface could looks like the following block. Don't forget to set the scope to perthread.

@Scope(value = ScopeConstants.PERTHREAD)
public class AppendedScriptsService implements IAppendedScriptsService {

    private final List
scriptFiles = new ArrayList();
    private final List
scripts = new ArrayList();

    @Override
    public void addScriptFile(String scriptFile) {
        this.scriptFiles.add(scriptFile);
    }

    @Override
    public void addScript(String script) {
        this.scripts.add(script);
    }

    @Override
    public List
getScriptFiles() {
        return this.scriptFiles;
    }

    @Override
    public List
getScripts() {
        return this.scripts;
    }

}

Build the Component
The Component was easy to write. It consists of just one method to render the scripts:
    // Inject the service we just wrote.
    @Inject
    private IAppendedScriptsService appendedScriptsService;


    // This method is called when component should be rendered. (Markup writer infos)
    void beginRender(MarkupWriter markupWriter) {


        // Output script files.
        if (!this.appendedScriptsService.getScriptFiles().isEmpty()) {
            for (final String file : this.appendedScriptsService.getScriptFiles()) {
                // There might be another (tapestry conform way) to create the source link. My way is
                String fileUrl = "MyAssetsServerURL".resolveRelative(file);
                markupWriter.element("script", "type", "text/javascript", "src", fileUrl);
                markupWriter.end();
            }
        }


        // Output script snippets.
        if (!this.appendedScriptsService.getScripts().isEmpty()) {
            for (final String script : this.appendedScriptsService.getScripts()) {
                markupWriter.element("script", "type", "text/javascript");
                markupWriter.getElement().text(script);
                markupWriter.end();
            }
        }


    }

That's all to implement. Just one last step to make this run:

Activate Service in AppModule
To activate your Service, following method must be inserted into AppModule.java (or just insert the binder-line if there is already such a method):
public static void bind(ServiceBinder binder) {
  binder.bind(IAppendedScriptsService.class, AppendedScriptsService.class);
}
User Component
To use this Component, you just have to add this into a class of a page or component: 
@Inject
private IAppendedScriptsService appendedScriptsService;


/**
* Append JavaScripts
**/
void setupRender() {
    this.appendedScriptsService.addScriptFile("UrlToScript");
    appendedScriptsService.addScript("var x = 'cool';");
}
The order of scripts and script files is preserved.

Do you have any comments? Let's discuss this in the Nabble Forum. I posted the Topic: "Append JavaScript at the end of my html file" - Component for Tapestry 5.1
EDIT: Howard Lewis Ship commented in the Nabble Forum:
The correct approach is to modify the DocumentLinker service (which is internal) to place the