11. Dezember 2015

Wordpress Rant

One quarter of the web runs on Wordpress. Millions of flies can not be wrong
...so I thought.

That was wrong...
  • disappointingly, 
  • irritatingly, 
  • gaspingly, 
  • outrageously 
...wrong.

I was setting up a new web site. OK, let's use Wordpress. Everyone is using Wordpress, There are so many cool Wordpress driven sites. And countless plugins. You can do everything with Wordpress.

Yes, you can, but only if you are a Wordpress expert and if you want to pay real money. Wordpress is not for you if you want to host a cheap (cost free) open source content management system on your own (virtual) server.

What's wrong?
  • It says it is a content management system, but the basic installation is just a blog system. There is no way to arrange/layout your content.
  • I can use plugins to create various numbers of columns in a page, but what they do is add meta tags to the content which formats columns. You always see the meta tags while editing the content. They are part of your article text. That's ugly. There is no way you call that a CMS and a WYSIWYG editor. 
  • Worse, column plugins create columns in the content area. Nothing more. No separator, no second content part. There is just one content area on a page.
  • I could pay about € 50 for a good layout plugin, but I don't want to. I moved to Wordpress, because it is "the dominant open source CMS". But it turns out. It is a blog engine, which can be upgraded to a CMS with real money and real effort.
  • The Wordpress team/community claims, that absolute URLs in the HTML are better than relative. I am not convinced, but it can be argued. But, even if you put absolute URLs into the HTML, then you definitely do not store references to uploaded images and internal links as absolute URLs. That's plainly a wrong design. 
  • It is not possible to change the site's base URL easily, because references to uploaded images and internal links are stored as absolute URLs. All internal links and images must be fixed when the domain name (base URL) changes. There is no good reason to store absolute URLs. You'd store them relative and insert the base URL while generating the HTML.
  • Short loop about the official docker image: It does not support email. Any real web site needs email. You can not omit it and call it the official docker image. Its worthless for non-experts. Nice try. Don't tell my why this is so. I know why. I know how to fix it. I can start another container with a mail server. But the "official" image is not prepared to interface with, say a postfix image. 
  • With help of another plugin I can use the built in SMTP client. But this is not for beginners, not for everyone. It needs an expert.
  • I can add custom fields to template pages. This way I can probably other additional content elements. But, after trying one hour I still don't see them. It's not for beginners. It needs a Wordpress expert. 
  • Very popular templates have major deficiencies. I chose a template with a full with image slider at the top of the start page. But, the thing is fixed to 3 (three) images. Not 1-3, not 3 or 4 or 5, not a variable number. Just: 3, WTF. I would not dare to publish this as a public template. Maybe hack this up for my personal use, but not as a template for everyone without a bit of flexibility. 
  • This can be fixed though. I just have to upgrade to the professional version of the template for additional money
  • Speaking of money: most templates want money for responsiveness. A responsive design should really be in the base version of the template. In which decade are they living? Mobile is mainstream, not optional, enterprise level, only for paying customers.
  • Everything can be fixed with plugins. But there are many plugins. Many plugins for similar features. Some do not work with your chosen template. You will find out after paying for the professional version. A steep learning curve. I try many plugins. I read many blog posts. I have to become an expert. Otherwise this blog software won't be a content management system. There is a plugin for everything. But only experts know which ones you need.  
  • While we are at it: many plugins claim to be so great. But plugins often have hidden deficiencies, which you discover after having invested significant time. That adds to the cost. It's not just trying different plugins. The time sink is in using a plugin until you discover a not so obvious flaw, then choosing a different one and start all over. 
  • Small but symptomatic: Enter the web site contact email address and it will appear as http://-link instead of a mailto:-link. This is the base installation with default template. Does not work. Of all these developers and users, did nobody ever click on the email in the header of the default template?
  • I won't start about how often Wordpress appears on the security lists, because a plugin was unsafe. But you need plugins for everything. At least with visualization and/or containerization you do not compromise your server anymore. Still, a mess.
  • I am not halfway through...
I am an expert in so many things. I do not want to become a Wordpress expert, just to make a web page. I am quicker coding a CMS myself in whatever language you want. Heck.

Please do not tell me that nothing is for free, that people have to live, that hard work must be payed, that it would be unfair to not pay them. That's not the point. They say: "WordPress is web software you can use to create a beautiful website, blog, or app. We like to say that WordPress is both free and priceless at the same time."

It is not. It is either free or beautiful. Wordpress costs very much time or significant money to make something beautiful and useful.

And yes, I know it is my fault. I could just pay someone 800 € per day to set it up and in the meantime earn 800 € per day with what I am already good at.

All I am saying is:
  • The base system is crap.
  • It is expensive too use.
  • The official docker image is a showcase, but otherwise useless.
Sorry, but this can't be serious.

It put up a good show. It got me.

happy_try_and_error_configuring()

7. Dezember 2015

Avira and VirtualBox Broken

tldr: Avira face punches VirtualBox. Need other virus scanner.

I was using Avira, because it is the best free virus scanner. And I am using VirtualBox for sandboxed Windows, local Linuxes, as docker container host, etc...

...but recently they stopped working together.

Over 2 weeks VirtualBox VMs were increasingly less likely to start. I needed some time and googling to find out that Avira prevents the VM from starting. Disable Avira: still nogo. Uninstall Avira: VirtualBox works. Reinstall Avira: VirtualBox broken again.

See: https://forums.virtualbox.org/viewtopic.php?f=6&t=68869

Time to move on to a different virus scanner.

_happy_googling()

6. Dezember 2015

Life is Strange

Habe in den letzten Tagen Life is Strange als Letsplay geschaut. Gefällt mir super. Ist wie Fernsehen. So soll es sein. Zurücklehnen vorspielen lassen.

Aber auch selber spielen ist sehr relaxt. Der nächste Level der Casualisierung.

Es spielt sich fast von selbst. Ab und zu mal was suchen, kleine Aufgaben lösen. Nichts wo ich nicht weiterkomme. Genau mein Schwierigkeitsgrad.

Nie wieder Open World. Spielen auf Schienen. Keine schwierigen Aufgaben zu lösen, Keine Leute umzubringen, keine NPCs, die warten bis sie gemetzelt werden. Ich kann die meisten Entscheidungen revidieren. Manche aber nicht. Das Spiel führt mich in der Story weiter. Interaktives Kino mit starken Emotion und Chillfaktor.

Gibt's für PS3. PS3 habe ich seit Assassin's Creed III. Das war das einzige Spiel was ich auf der PS3 gespielt habe (und das nur halb). Also kann ich mit Life is Strange  meine Investition von 400 € pro Game auf 200 € pro Game verringern. Reichgespart.

_happy_chilling()

PS: Das Game auf die PS3 zu bringen war auch schon eine Quest-Reihe. Ich sach nur Controller aktivieren, System Update, zu alter Account, problematische Payment-Optionen, mehr Updates, Downloads, mehr Downloads, Updates, Shop verlassen, wieder rein, Reboot, Update, immer wieder Auflösung Umschalten und dann viele Screens (Engine, Publisher, Developer, alle toll) bis das Game endlich startet. Unchillig das.

23. Oktober 2015

Galactic Developments ist umgezogen auf einen neuen Server als KVM

Bisher war die Galactic Developments Website ein Apache Virtual Server auf meinem alten Hetzner Rootserver. Den hatte ich vor 6 Jahren bei einer keine-Setupgebühr-Aktion gebucht (und dann 3 Monate lang nicht installiert, was die keine-Setupgebühr-Aktion für mich ad-absurdum geführt hat, für Hetzner nicht). Inzwischen ist das Betriebssystem (Debian Sarge) aber schon aus den Security-Fixes raus gelaufen.

(Ich finde Sicherheitsaktualisierungen für nur 3 Jahre etwas kurz. Naja, ist ja Open Source. Wenn es einem nicht gefällt, dann einfach nicht benutzen oder selbst fixen, wie man so schön sagt, jedenfalls nicht meckern.)

Die Rechner der Rabatt-Aktion waren damals etwas schwach auf der Brust. Das stört nicht, wenn man nicht viel Traffic hat, aber heutzutage will man virtualisieren und mehrere Server gleichzeitig laufen lassen. Dafür ist ein ganzes GB RAM nicht genug. Der neue Server ist wieder bei Hetzner, hat aber 2 TB Platte, 32 GB RAM, 8 CPUs incl. Hyperthreading. Das sollte reichen für ein paar virtuelle.

Ich kann Debian, also weiter Debian-stable, d.h. Jessie 8.2.

Zum Virtualisieren kvm und libvirt drauf. Ein 2 GB Image für das Guest-Template erstellen mit einer Debian-minimal Installation ohne alles außer sshd. Die VMs sind nur über das interne "default" Netzwerk zu erreichen. Alle VMs bekommen statische IP Adressen vom internen DHCP.

Ein VM als Reverse-Proxy, der HTTP-Requests an verschiedene virtuelle Maschinen weiterleitet. Dafür eine iptables-Konfiguration per qemu-Hook, die immer dann die 2 iptables-Regeln setzt/löscht, wenn die VM startet/stoppt. Auf dem Proxy ein nginx, der alle Anfragen für www.galactic-developments.de an die Galactic Developments VM weiterleitet.

Der Galactic Developments Server bekommt eine eigene VM. Hier mit Apache, weil ich Apache schon kann und nicht zu viele Konfiguration ändern muss, dachte ich. Tatsächlich haben sich die Apache Entwickler ein neues Sicherheitskonzept einfallen lassen und erst mal geht gar nichts, bis ich herausfinde, dass man einen neuen Befehl (Require) braucht. Zusätzlich zum Galactic Developments Apache virtual Server gibt es noch einen CatchAll virtual Server, der Adressen wie galactic-developments.de (ohne www.) und *.galactic-developments.com auf den Hauptserver umleitet (401/permanent).

Dateien und Daten der Galactic Developments Website sind in Subversion. Das bleibt erstmal so bis sich die Community mehr beteiligt. Dann will ich git nicht im Wege stehen. Also: Repository auschecken und Apache-config darauf zeigen lassen. Bisher waren SVN Server und Website auf dem gleichen Rechner. Ein Subversion post-commit Hook hat automatisch die Website svn update'd. Poor man's Continuous Delivery. Das geht jetzt nicht mehr, weil es verschiedene Rechner sind und später - wenn der SVN Server auch umgezogen ist - gleicher Rechner, aber getrente VMs. Deshalb muss der post-commit Hook jetzt das Deployment anders triggern. Ich mag Trigger per HTTP-Request. In diesem Fall: ein Einzeiler-PHP in der Galactic Developments Website (Name lang und geheim, quasi das Passwort im Namen, Security by Obscurity), das ein lokales Shellscript startet, das wiederum "svn update" macht. Drei Einzeiler hintereinander. Man muss noch die Benutzer-Grenze überwinden vom wwwdata-User des Apache zum Eigentümer des Repositories. Deshalb wird das Shellscript mit sudo ausgeführt. Dafür eine Zeile in /etc/sudoers. Bei jedem svn commit ruft der post-commit Hook das update PHP-Script in der Zielwebsite per wget ("-O -" nicht vergessen) auf.

Dann noch DNS für www.galactic-developments.de umbiegen von rama.wolfspelz.de auf fred.wolfspelz.de und Galactic Developments ist umgezogen.

Das hört sich alles locker flockig an, hat mich aber mehrere Tage gekostet. Es gibt fast keine Anleitungen für libvirt/kvm OHNE lokales Display und ohne VNC. Auch virt-manager usw. alles nett gemeint, aber ich will kein Desktop auf meinem Server nur zum Installieren. (Wer bis hier gelesen bekommt von mir ein nagelneues Notebook). Genauso das Netzwerk: entweder es wird nicht erwähnt, was blöd ist, wenn man die VM vom Netzwerk installieren will oder es wird bridge-Networking vorgeschlagen, was bei mir einfach nicht wollte. Dabei geht das "default" Netzwerk super. Man muss es nur anschalten. Das könnte mal irgendwo stehen. Ein qemu-Hook statt bridge-Netz sehe ich nicht als Hack an, im Gegenteil.

PS: rama.wolfspelz.de war keine Frühstücksmargerine, sondern ein 50 km langes Alien-Raumschiff.

_happy_migrating()

21. September 2015

Feature Flags

(Image: bytearrays.com)
In one of my projects I just stumbled across the topic "Feature Flags".

Feature flags (aka Feature Toggle, aka Feature Switch) let you enable program/web site features selectively. This is especially important in continuous delivery environments where code is always committed and deployed even if a feature is not ready for prime time. It can be useful if you have a large user base and want to introduce the feature incrementally by enabling the feature for sub-sets of users. Feature flags are useful in anything beyond toy apps.

Feature flags are not new. They are widely known since Martin Fowler blogged about the pattern. He did not invent them. Many good developers have been using feature flags or alike for a long time. But Martin Fowler recognized feature flags as a remarkable pattern. Writing about feature flags was a good idea, because other developers who are not aware of the pattern can learn from it. That was 2010.

Guess what, in 2006 a lonely Weblin programmer added feature flags to the Weblin portal. Weblin has been using feature flags from the start.

_happy_flagging()

15. September 2015

ZEIT online Relaunch - Undo

ZEIT online hat gerelauncht. Jetzt irgendwie dunkel und schmaler als vorher. Gefällt nicht. Kann man aber fixen. 

Links: ZEIT nach Relaunch, 
Rechts: gefixt: wieder weiß, ohne Rand 


Wie?

Mit Tampermonkey, eine Chrome Erweiterung. Tampermonkey führt auf jeder Webseite Javascript aus.

Bei ZEIT online muss man nur ein paar CSS Anweisungen verändern: den Hintergrund vom <body>-Tag wieder in weiß und der Rahmen vom Content-<div> weg:

body { background-color: #ffffff !important; } 
.page__content { box-shadow: none; }

Man braucht noch etwas Javascript, um das CSS einzufügen. Hier das ganze Tampermonkey-Script für ZEIT online:

// ==UserScript==
// @name        ZEIT
// @namespace   http://wolfspelz.de/
// @version     0.1
// @description Restore white background
// @match       http://www.zeit.de/*
// @copyright   2015+, wolfspelz
// ==/UserScript==

function addGlobalStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

addGlobalStyle('body { background-color: #ffffff !important; } .page__content { box-shadow: none; }');

_happy_tampering()

Für Firefox: Greasemonkey

12. August 2015

Nie wieder Smooth Gestures für Chrome

(Blogpost, damit ich es mir selbst merken kann)

Ich war immer sehr zufrieden mit der Chrome Extension "Gestures for Google Chrome". In letzter Zeit ist Gestures for Google Chrome anscheinend manchmal abgestürzt. Jedenfalls war sie immer wieder nicht aktiv, musste von Hand aktiviert werden.

Ich hatte noch von früher "Smooth Gestures" installiert, aber nicht aktiv. Probeweise mal wieder aktiviert. Macht in allen Tabs haufenweise Requests an yieldsquare.com (gefühlt 10 pro Sekunde).

Also: nie mehr aus Versehen zu Smooth Gestures greifen.

_happy_gesticulating()

6. August 2015

Minimalinvasiver Bootstrap Bestätigungsdialog (Confirm Modal Dialog)

Ab und zu braucht man bei Webanwendungen einen Bestätigungsdialog, bevor die Benutzerin eine Aktion auslöst, zum Beispiel "Do you really want to delete …"


Das geht mit Bootstrap sehr schlank und fast ohne Änderungen am Code. Kein Umstellen eines Links auf Button mit click-Aktion, kein Aufruf einer Javascript Dialog-Klasse, keine extra Funktionen als Callbacks, die auf Dialog-Button-OK oder –Abbrechen reagieren, sondern…

…etwas HTML (für den Dialog) und 2 Zeilen JavaScript zum Verdrahten.

Der Trick: Angenommen es gibt einen Link, dem der Confirm-Dialog vorgeschaltet werden soll. Das href-Attribut des Links wird zum "OK"-Button des Dialogs weitergereicht und der Link stattdessen zum Öffnen des Dialogs benutzt.

Ein Klick auf:

Delete

<a href="/Offer/Delete">Delete</a>

würde das Angebot (Offer) sofort löschen. Das soll durch einen Bestätigungsdialog abgesichert werden:

Dafür muss man 3 Dinge tun:

1. Dem Link sagen, dass er den Dialog aufrufen soll

<!—data-target und data-toggle aktivieren den Dialog beim Klick-->
<a href="/User/Delete" data-target="#ConfirmDialog" data-toggle="modal" id="DeleteButton">Delete</a>

2. Den Dialog definieren (irgendwo im Body)

<!-- Modal dialog -->
<div class="modal fade" id="DeleteDialog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
                <h4 class="modal-title">Echt jetzt?</h4>
            </div>
            <div class="modal-body">Willst du das Angebot wirklich löschen?</div>
            <div class="modal-footer">
                <button type="button" class="btn btn-primary btn-ok">OK</button>
                <button type="button" class="btn btn-default" data-dismiss="modal">Abbrechen</button>
            </div>
        </div>
    </div>
</div>

3. Alles verdrahten:

// Beim ursprünglichen Link:
$('#DeleteButton').each(function () { $(this)
  // Link als data-href sichern
  .data('href', $(this).attr('href'))
  // Link durch # entschärfen
  .attr('href', '#');
});
// Beim Öffnen des Dialogs:
$('#DeleteDialog').on('show.bs.modal', function (e) { $(this).find('.btn-ok')
    // …das gesicherte href-Attribut zum OK-Button weiterreichen 
  .attr('href', $(e.relatedTarget).data('href'))
    // Beim OK-Klick zum href navigieren
  .click(function () { location.href = $(this).attr('href'); });
});

Das sieht viel aus, aber in der Praxis schreibt sich das so:

$('#DeleteButton').each(function () { $(this) .data('href', $(this).attr('href')) .attr('href', '#'); });
$('#DeleteDialog').on('show.bs.modal', function (e) { $(this).find('.btn-ok') .attr('href', $(e.relatedTarget).data('href')) .click(function () { location.href = $(this).attr('href'); }); });

_happy_confirming()

PS: Nur eins der vielen kleinen Themen, die so ein Web-Projekt aufwirft: www.essenbeifreunden.com

23. Juni 2015

Was geht ... Galactic Developments

Wenn ihr Euch fragt, warum hier keine Posts mehr kommen...

Momentan konzentriere ich meine Freizeitaktivitäten auf Galactic Developments. Als Kreativ-Übung schreibe ich jeden Tag eine neue Idee auf.

Ich schreibe pro "Story" maximal eine Seite. Dafür aber jeden Tag eine. Ich versuche mir jeden Tag etwas Neues einfallen zu lassen, eine neue Erfindung, eine Entdeckung, ein neues Ereignis. Und das dann in maximal 1000 Wörtern zu formulieren.

Die Action ist hier:
Das ist zur Zeit mein Haupt-Hobby: Story ausdenken, schreiben, rechtefreie Bilder suchen, Bild machen, Blog posten, Timeline-Eintrag comitten (Subversion), Facebook und Twitter post-Bilder machen, Facebook/Twitter posten, Facebook Gruppen und Follower betreuen. Daneben bleibt nicht viel Freizeit.

_happy_writing()



22. April 2015

Interview auf dem Deutschen Science Fiction Portal

Auf deutsche-science-fiction.de ist mein Interview zum Thema Galactic Developments erschienen. Sven Kloepping vom  Deutschen Science Fiction Portal hat mir einige Fragen zugeschickt, die ich dann nach bestem Wissen und Gewissen beantwortet habe.

Eine Frage hat mir besonders zu Denken gegeben: Inwieweit fließen reale Forschungsergebnisse und Entdeckungen in Ihr Projekt ein?

Anfangs dachte ich, dass ich als naturwissenschaftlich interessierter Physiker mein Wissen oft verwende. Immerhin lese ich die Zeitschrift der Deutschen Physikalischen Gesellschaft, Spektrum der Wissenschaft und mehrere Science Blogs. Ich verfolge die neueste Hochenergiephysik, war sogar live (im Stream) bei der Veröffentlichung des Higgs und bei der Landung von Philae dabei.

Bei genauerem Nachdenken ist mir dann aber aufgefallen, dass ich neuesten Erkenntnissen eher skeptisch gegenüberstehe. Damit meine ich nicht, dass ich sie anzweifle. Neueste wissenschaftliche Erkenntnisse sind (fast) immer korrekt. Aber es kommt immer wieder vor, dass sie später anders interpretiert werden können. Und da ich bei Galactic Developments aus der Sicht der Zukunft schreibe, bin ich vorsichtig mit brandneuen Erkenntnissen, die später relativiert werden könnten. Wenn ich Galactic Developments schreibe, dann aus der Sicht des Artikels ja schon ein paar hundert Jahre vergangen und es sind neue neueste Erkenntnisse dazugekommen, die die alten neuesten Erkenntnisse vielleicht in einem anderen Licht erscheinen lassen.

Vorsicht vor Extrapolation:

Die erste Eisenbahn war mit 28 Stundenkilometer wahnsinnig schnell. Der Begriff "schnell" hat sich sicher gewandelt. Heute ist ein Zug mit 100 km/h nicht besonders schnell. Schreibt man also, dass ein Regionalzug im 25. Jahrhundert 500 Stundenkilometer "schnell" ist, dann mag das heute toll klingen. Ist auch vermutlich nicht mal falsch. Aber Thema verfehlt, weil alles ganz anders ist und Räder nicht mehr auf Metall rollen. Vielleicht nicht einmal auf Magneten schweben. Merke: einfaches Extrapolieren geht nicht. Ich versuche deshalb disruptive Änderungen zu erfinden, statt die neuesten Erkenntnisse zu extrapolieren.

Vorsicht vor impliziten Annahmen:

Die Legende sagt, dass Galileo Galilei auf den Schiefen Turm von Pisa gestiegen ist, zwei Metallkugeln fallen gelassen hat und so die Fallgesetze erfunden hat: alles fällt gleich schnell und zwar immer schneller mit einer Beschleunigung von 9,81 m/s2.

Das war damals die neueste Erkenntnis. Das ist auch immer noch richtig. Aber heute würden wir das nicht so formulieren, denn das stimmt nur auf der Erde und auch da nur auf Meereshöhe. Auf dem Mond sieht das anders aus (nur ein Sechstel davon), in tiefen Bergwerken auf der Erde ist es auch weniger (im Promille Bereich). In einer Erdumlaufbahn fällt man gar nicht auf die Erde.

Newton und Kepler haben die Gesetze der Gravitation und Bewegung verallgemeinert, so dass sie auch auf dem Mond und in tiefen Bergwerken gelten. Das macht die neuesten Erkenntnisse von Galileo nicht falsch, aber rückblickend hat Galileo "nur" einen Spezialfall beschrieben, keinesfalls allgemein gültige physikalische Gesetze.

Und dann kam Einstein und erklärte, dass auch die neuesten Erkenntnisse von Newton und Kepler nur eingeschränkt gelten. Sie stimmen, aber nur solange sich alles langsam bewegt, wenn die Schwerkraft nicht sehr stark ist und man kann sie überhaupt nur auf normale Materie anwenden, aber z.B. nicht auf Licht.

Einstein schrieb neue Formeln auf, die auch dann noch gelten, wenn sich sehr schwere Massen sehr schnell bewegen oder bei Lichtteilchen, die gar nichts wiegen und sogar mit Lichtgeschwindigkeit fliegen. Es stellte sich heraus, dass auch die Formeln von Newton und Kepler Spezialfälle einer allgemeineren Theorie waren. Und die neuesten Erkenntnisse waren plötzlich alt. Nicht falsch, aber nicht allgemein anwendbar.

Also Vorsicht vor den neuesten Erkenntnissen der Wissenschaft. Die sind richtig, können sich aber als Spezialfälle herausstellen und später etwas altbacken wirken. Man bekommt sogar den Eindruck, dass die Aussage von Galileo "alles fällt gleich schnell" ein bisschen falsch ist. Das liegt aber nur daran, dass er es so allgemein formuliert hat und dabei ein paar Annahmen weggelassen hat, nämlich: "1. auf der Erde und 2. auf Meereshöhe, 3. solange sich alles langsam bewegt und 4. es sich um normale Materie handelt". Tatsächlich waren Galileo diese Annahmen nicht bewusst. Sie waren implizit. Sonst hätte er vermutlich gleich eine allgemeinere Theorie formuliert und Newton, Kepler und Einstein arbeitslos gemacht.

Für mich ist deshalb die wichtigste Erkenntnis, dass alles was heute brandneu ist, später überholt sein kann, auch wenn es vorher nicht falsch war. Vorsicht vor impliziten Annahmen.

_happy_assuming()

7. Februar 2015

How to Make Your Site Responsive and Mobile Friendly in Minutes

Assumed you have a web site for years. It has a traditional layout with a header, a menu sidebar, and a content area. The menu sidebar and also the header do not fit very well on a smartphone screen.

We need a scrolling friendly vertical design. It should appear automatically whenever the screen width is the size of a smartphone. We need an overlay menu instead of the menu sidebar.

And we need it quickly without a complete redesign. 

This is what I just did for my old web site http://www.galactic-developments.de

(Because it went so smooth and easy, I'll report what I did. Maybe it helps someone. Paying back to the Web.)


Starting with a typical nice static, not responsive, not smartphone compatible design:




We will turn it into a responsive design with popup menu:




...in 10 minutes.

The Basic Principle:

HTML5/CSS3 offers a simple mechanism to switch designs based on device properties: media queries. A media query applies the enclosed CSS only if the query condition is met. 

I insert this into the HTML of the page template of my CMS (I could also add it to my CSS file):

@media screen and (max-width:700px) {
  // here will be CSS
}

Media Queries override general CSS-styles. Inside the curly braces I will now redefine some of the styles. By redefining I can make elements disappear, change sizes, and change the layout.

The above CSS means, that the styles will be applied to screens (not when printed) and which are smaller than 700 pixels.

Things to do:

1. Remove Header, Footer, and Frame

The frame in the background must disappear. The background consists of three <div>s with a background image each. Top and bottom <div>s also contain other graphics and buttons. They will all disappear. The center <div> also has my text and the menu. The center <div> will stay. But it will loose it's background. Luckily these three <div>s already have IDs. So I can add these lines inside the media query:

  #bgtop { display: none; }
  #bgcenter { background: none; }
  #bgbottom { display: none; }

... which makes my new media query CSS look like:

@media screen and (max-width:700px) {
  #bgtop { display: none; }
  #bgcenter { background: none; }
  #bgbottom { display: none; }
}

See, how the header and the vertical frame disappeared:


2. Menu

The menu should not be statically on the left side. It should be invisible at start and accessible from a menu button. If I press the menu button, then the menu should appear.

I decided, that the menu button should be in the top right corner of the screen and the menu will appear on the right side. Reason: the button in the top right corner will cover less of the text. There is free space at the top right while the top left always has text. I move the menu from the left side, which is also left of the text, to right side and above the text. My menu <div> has the ID "menu":

#menu {
  position:fixed; right:0px; top:0px; 
  background-color:#ffffff; padding:4px; 
}

The position will to be fixed even if the page is scrolled. There is a 4 pixel padding which is filled by a white background color for a small distance between the content below and the menu:


If you make the window wide, then it has still the original design.

3. Menu Button

This will be my menu button:


I insert the button <div> into the HTML (I put it just before the header <div>):

<div id="menubutton"></div>

Add CSS for the menu button:

#menubutton {
  display: block; position:fixed;
  right:4px; top:4px; width:24px; height: 24px;
  background:url(img/menubutton.png);
}

Shift the the menu down to make room for the menu button:

  #menu { margin-top:28px; }

Add a small JavaScript section to the HTML to toggle the visibility of the menu, when the menu button is pressed (jquery would be overkill, could use jquery for fade/slide animations, though):

<script>
 
document.getElementById('menubutton').onclick = function()
  {     
    var m = document.getElementById('menu'); 
    m.style.display = (m.style.display == 'block' ? 'none' : 'block');
  };
</script>

    When the page loads, then the menu should not be visible (it will be shown by the menu button):

      #menu { display: none; }


    4. Device Scaling:

    Pixel densities on mobile devices are usually higher than on desktop/laptop screens. And they differ. But all devices should show about the same amount of text. This means, that a scaling factor must be applied, which scales the page depending on the DPI of the device. The command for that comes as a meta tag. Add it to the HTML <head> section (of the web site template):

    <meta name="viewport" 
      content="width=device-width,initial-scale=1,user-scalable=no"
    />

    That's basically it:

    5. Additional Tweaks:

    Pixel densities on mobile devices are usually higher than on desktop/laptop screens. So I changed the baseline font size from 12px to 14px (this might be more effort depending on your existing CSS). In my case:

    * { font-size:14px; }

    On small smartphone screens, like old iPhones, the menu is too long. I remove some menu entries, which are not really important. I do this by assigning IDs to menu entries like. I changed:

    <li><a href="stuff.html">Wallpapers</a></a>

    to:

    <li id="menuWallpapers"><a href="stuff.html">Wallpapers</a></a>

    ...and add this to the CSS:

      #menuWallpapers { display: none; }

    As you see in the screen shot, my text is too wide. The reason is, that in my original design I assigned a fixed width to the content area, so that the text does not flow outside the border. Now, the text width should adjust to the device width. In my case, there are two <div>s to be fixed:

      #bgcenter, #main { width: auto; }

    I want to remove the page URL from the top. I don't think it's very useful, especially on a mobile screen. The page URL is only on the start page. It is a page content, not in the template of all pages. Still, I can hide it with a CSS in the same place as the other CSS tweaks.

    In other words: a style which is only used by a single page is configured globally by the media query CSS section. No problem. Since the link is inside a <div class="link">:

      .link { display: none; }


    A small shadow for the menu is no mistake:

      #menu {
        -webkit-box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 1);
        -moz-box-shadow:    0px 0px 8px 0px rgba(0, 0, 0, 1);
        box-shadow:         0px 0px 8px 0px rgba(0, 0, 0, 1);

      }

    Result:




    Summary:

    The complete CSS:

    @media screen and (max-width:700px) {
      #bgtop { display: none; }
      #bgcenter { background:none; }
      #bgbottom { display: none; }
      #menu { margin-left: 0px; }
      #menu { 
        position:fixed; right:0px; top:0px; 
        background-color:#ffffff; padding:4px; 
      }
      #menubutton {
        display: block; position:fixed;
        right:4px; top:4px; width:24px; height: 24px;
        background:url(img/menubutton.png);
      }
      #menu { margin-top:28px; }
      #menu { display: none; }
      * { font-size:14px; }
      #menuWallpapers { display: none; }
      #bgcenter, #main { width: auto; }
      .link { display: none; }
      #menu {
        -webkit-box-shadow: 0px 0px 8px 0px rgba(0, 0, 0, 1);
        -moz-box-shadow:    0px 0px 8px 0px rgba(0, 0, 0, 1);
        box-shadow:         0px 0px 8px 0px rgba(0, 0, 0, 1);

      }
    }

    A bit HTML and JavaScript for the menu button:

    <div id="menubutton"></div><script>
      document.getElementById('menubutton').onclick = function()
      {     
        var m = document.getElementById('menu'); 
        m.style.display = (m.style.display == 'block' ? 'none' : 'block');
      };
    </script>
    A meta tag in the HTML <head>:

    <meta name="viewport" 
      content="width=device-width,initial-scale=1,user-scalable=no"
    />

    _happy_scaling();

    Of course, there are other ways to make a responsive web site. You could use a mobile friendly CSS package, like bootstrap. But that probably means, that you will redesign your site, which won't be done in an hour.

    Of course, there are more modern ways to program the menu button. You could use jquery and this JavaScript:

      $('#menubutton').click(function() { $('#menu').fadeToggle(); });

    But you have to include 100 kB jquery library:

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>

    13. Januar 2015

    Animated GIF

    Auf dem 31C3 hat Felix Mütze einen netten Vortrag gehalten über das Zombieleben von GIF. Auf seinem Blog hat er noch mehr zum Thema GIF gesammelt. Da ich bei den Anfängen von animated GIF dabei war haben wir uns danach unterhalten und ich habe versprochen noch ein paar Details aufzuschreiben. Hier also...

    Animated GIF

    Anfang 1995 hatten wir an der Universität Ulm eine Modelleisenbahn ins Netz gestellt. Die Besucher konnten den Zug im Kreis fahren lassen. Eine Kamera machte Bilder. Dafür musste man natürlich im Browser RELOAD drücken. Wir wollten, dass man ein Live-Video bekommt und haben nach Möglichkeiten gesucht, das mit der damaligen Technik zu machen. So sind wir auf GIF89a gestoßen (Konrad Froitzheim und ich). Ich habe ein animated GIF im Hex-Editor zusammengebaut. Dann das GIF den aktuellen Browsern vorgeworfen (XMosaic, Netscape Navigator) und es geschah: nichts, außer einem Bild. GIF wurde nur als Bildformat, nicht als Animation unterstützt.  

    Noch schlimmer: Mosaic wollte alle Bilder komplett runterladen, bevor das erste Stück HTML angezeigt wurde. Das war offensichtlich nicht geeignet für Live-Video. Live-Video wollten wir nämlich als ein nicht endendes animated GIF vom HTTP-Server zum Browser schicken. Aber Netscape 1.1 zeigte ein interessantes Verhalten: progressives Dekodieren. Damals sehr innovativ. Immer wenn neue Daten ankamen, wurde mehr angezeigt. Damit war klar, dass die Softwarearchitektur für Animationen geeignet war. Man musste das nur noch Netscape klar machen.

    Deshalb habe ich das Feedback Formular von Netscape auf die Webseiten der Modelleisenbahn kopiert, wo dann viele Besucher der Modelleisenbahn ihre Bitte um GIF-Animationen an Netscape (vor-)formuliert abgeschickt haben. Eines Tages meldete sich Scott Furman von Netscape und bat um mehr Informationen zum Thema animated GIF. Ich schickte ihm alles und ein paar Beispieldateien. Kurze Zeit später kam Navigator 2.0 mit animated GIF. Scott hatte dabei den Loop-Marker erfunden. Da GIF98a als Stream gedacht war und kein Loop enthielt, machte er den Loop-Marker als GIF-Comment. Das war wirklich eine coole Idee und hat überhaupt erst die ewig loopenden Icons ermöglicht. 

    Live Video

    Netscape's Marketing hatte wohl nichts davon erfahren, denn die großen Features waren Javascript, Framesets und Cookies. Von Animationen keine Rede. Aber sie gingen. Sowohl als Endlosschleife, als auch im Stream-Modus wo der Server die Verbindung hält und immer mehr Bilder schickt.
    Ich schrieb also einen Videoserver, der den Stream von der Kamera in ein unendliches animated GIF kodierte (in C als nph-cgi). Nach dem ersten Vollbild, suchte der Videoserver die geänderten Bereiche, kodierte das Bild nur für diese Rechtecke neu und schickte es auf die Leitung. Der URL des Streams verpackt als <img src=> im HTML zeigte tatsächlich ein Video. 

    Die Besucher der Modelleisenbahn konnten etwa im Sekundentakt sehen, wie der Zug um das Oval "hopste". Unglaublich, dass es dann plötzlich einfach so klappte. Das erste Live-Video im Web. Und für lange Zeit das einzige echte "live" Video im Web überhaupt.

    Dann kam PNG und ich hatte damals Kontakt mit Thomas Boutell. PNG ging ja durch viele Revisionen, aber im Thomas wollte keine Animationen einbauen. "Erst machen wir das Bildformat. Wenn sich das durchgesetzt hat, dann Animationen". Er hatte befürchtete, dass die Animation es komplizierter machen würde und so PNG schaden würde. Vielleicht hatte er Recht. Aber am Ende hat genau die Animation gefehlt, um GIF abzulösen. Ein Browser-Entwickler, der parallel zu animated GIF jetzt PNG einbauen will, fragt sich eher, was er mit den Codestellen machen soll, wo GIF noch mehr Bilder liefert, PNG aber nicht. Also muss er sie sinnvoll leer lassen. Wenn er dann nochmal den Code anfassen muss, weil jetzt doch PNG Animationen kommen sollen, muss man die stillgelegten Stellen wieder umbauen. Noch schlimmer, wenn die PNG Animation MNG heißt und einen anderen MIME-Type hat. Dann muss man neben PNG noch einen fast identischen Dekoder integrieren, der aber jetzt doch so wie GIF funktioniert. Das ist alles etwas mühsam. Jedenfalls mühsamer, als wenn man gleich parallel zu aGIF den aPNG Code eingebaut hätte. 

    Klar war aber auch, dass GIF mit den Farbpaletten nicht ideal für Fotos und Kamerabilder ist. Deshalb lag es nahe, das Animationsprinzip von GIF auf JPEG anzuwenden. Eigentlich ja ganz einfach. GIF hat einen Header, dann ein Vollbild, dann Teilbilder, die per Koordinate irgendwo im Screen einen Teil überdecken können. Also eine differentielle Kodierung ohne weitere Optimierung, aber viel besser, als immer Vollbilder zu übertragen. 

    Animated JPEG

    JPEG ist ganz ähnlich strukturiert wie GIF (und PNG). Der Kompressionsalgorithmus ist ganz anders. Aber beide bestehen aus sogenannten Chunks (Datenpaketen), die hintereinander gehängt werden. Der Header ist ein Datenpaket oder auch mehrere. Die Daten, die ein Bild ergeben sind in einem Datenpaket. Kommentare, Loop-Marker, Geo-Koordinaten, Datum sind alles einzelne Datenpakete. Bei animated GIF kommen nach dem ersten Bild-Paket noch weitere Bild-Pakete. Die haben alle eine Größe und eine Koordinate und legen sich quasi über die vorherigen Bilddaten. Zusammen mit dem Delay ergibt das eine Animation.

    Genauso wäre es bei JPEG. Normalerweise ist bei JPEG ein Header, dann ein Vollbild, dann Schluss. Würde man nach dem Vollbild weitere Chunks mit Teilbildern schicken, jedes mit einem Delay, Koordinate und Größe, dann wäre die Animation schon fertig. Noch ein Loop-Marker und fertig ist die Truecolor-Animation.

    Das haben wir programmiert (Michael Merz, Holger Bönisch und ich) und Tom Lane angeboten, der den JPEG-Code verwaltete. Leider hat er ihn abgelehnt mit der Begründung, dass MPEG für Animationen vorgesehen ist. Da hat er recht. Aber das hätte man von GIF auch behaupten können. Hat man aber nicht. Ein MPEG-Dekoder ein unglaublich aufwändiges und erstaunlich empfindliches Stück Software. Kein Browserhersteller hat MPEG direkt integriert. Das kam immer nur als Plugin oder mit Quicktime. Viele Movies waren inkompatibel und brachten den Browser zum Absturz. Als Microsoft den Browsermarkt dominierte haben sie es auch nicht geschafft, Movies auf einfache Art in Seiten einzubinden. Microsoft ist so spektakulär gescheitert, dass Flashplayer mit einer Konkurrenztechnologie für lange Zeit Truecolor-Animationen dominierten. 

    Es hat 15 Jahre gedauert hat, bis man endlich ein MPEG per <video src=> einfach einbinden konnte, ohne Flash-Player und viel Aufwand. Wäre damals JPEG parallel zu aGIF als Animationsformat erweitert worden, dann hätten wir seit 1997 eine einfache Art, Truecolor-Animationen zu machen. GIF wäre höchstens noch für kleine animierte Icons zuständig, animated JPEG für "kurze" Truecolor-Animationen mit anständiger Komprimierung und richtige Video-Codecs für ganze Filme mit der besten Komprimierung. 

    Eigentlich wäre animated GIF durch animated JPEG abgelöst worden. Und eigentlich wäre das auch heute noch eine gute Idee. 

    Hier mein erstes im Hexeditor gebautes animated GIF mit dem ich damals Browser getestet habe. Wenn es eine 1 zeigt, dann kann der Browser kein animated GIF. Bei 2 kann er. 

    Heute ist es gar nicht so einfach die 1 zu sehen.


    Das wahrscheinlich erste GIF-Movie. Zumindest das erste nach der Wiederentdeckung von animated GIF. Auf jeden Fall das erste, das von einem Web-Browser dargestellt wurde.

    Ich brauchte was Längeres, als die kleinen Testbilder, um die Streaming-Fähigkeit von Browsern zu testen. Erstellt durch Copy-Paste jedes einzelnen Frames.




    Ein Bild der Modelleisenbahn. Von rechts nach links: PC für Zugsteuerung, Kamera, Mac mit Videoserver:


    Die zugehörige Website (klassisches Web 1.0 Design): 




    happy_streaming()


    Nebenbei bemerkt: 300.000 Besucher bis Mai 1997. Schon eine gute Zahl für damals.