pvitt's bits and pieceshttps://blog.pvitt.de/index.atom2019-10-23T17:00:27ZpvittNikolavirtual-pdf-printerhttps://blog.pvitt.de/posts/virtueller-pdf-drucker-mac/2019-10-23T18:13:29+02:002019-10-23T18:13:29+02:00pvitt<div><p>Es gibt Situationen, da braucht man Dinge, die man eigentlich schon lange hinter
sich gewähnt hat. So heute, als ich ein PDF-Formular in in PDF drucken wollte.
Auf dem Mac gibt es dazu im Druck-Dialog die Möglichkeit, das Dokument als PDF
zu speichern. Funktioniert prima, so lange man nicht versucht, ein Formular
meiner Zentralverwaltung zu drucken. Denn dieses Formular wird ausgefüllt und
anhängig von den Eingaben werden daraus zwei bis fünf neue Dokumente generiert
und diese dann an den Drucker gesendet. Dazu gibt es im Formular extra den <strong>Nur
diesen Button zum Drucken verwenden</strong>-Button. Problem ist nur, dass der so
geöffnete Druck-Dialog es irgendwie nicht schafft, die Ausgabe in ein PDF
umzuleiten. Also begab ich mich auf die Suche nach einem virtuellen PDF-Drucker
für macOS.</p>
<p>Nach gefühlt zweihundert Suchtreffern der Kategorie "Das geht doch direkt aus
dem Druck-Dialog heraus" bin ich dann auf <a class="reference external" href="https://bitbucket.org/codepoet/cups-pdf-for-mac-os-x/wiki/Home">CUPS-PDF</a> und in dessen Dunstkreis auf
<a class="reference external" href="https://onflapp.wordpress.com/vipriser/">VipRiser</a> gestoßen. Da CUPS-PDF wohl den Umweg über PS zu PDF macht, VipRiser
aber <em>native</em> PDFs erstellt, habe ich letzerem den Vorzug gegeben. Die
Installation ist allerdings etwas hakelig:</p>
<ol class="arabic simple">
<li>Das zip-Archiv entpackt und die VipRiser-App nach Applications verschoben</li>
<li>VipRiser gestartet</li>
<li>Nun wird per Dialog hingewiesen, dass der Druckertreiber veraltet sei.
Veraltet ist er eigentlich nicht, eher nicht vorhanden. Aber sei's drum,
Treiber wird installiert.</li>
<li>Nachdem der Treiber installiert wurde, öffnet sich ein Dialog, wie man nun
den Drucker einrichten soll. Dort wird empfohlen, den bei der
Treiberinstallation bereits angelegten Drucker in den Systemeinstellungen
wieder zu löschen und statt dessen einen neuen Drucker anzulegen. Von diesem
Schritt rate ich ab, denn der von mir angelegte Drucker funktionierte nicht.
Nach einer Neuinstallation des Treibers funktionierte aber der während der
Installation erstellte Drucker problemlos.</li>
</ol>
<p>Nun bin ich also in der Lage, das durch seltsame PDF-Formulare deaktivierte
<em>Save as PDF</em> durch die Verwendung eines PDF-Druckers zu umgehen. Nicht schön,
aber es funktioniert.</p></div><div><p>Es gibt Situationen, da braucht man Dinge, die man eigentlich schon lange hinter
sich gewähnt hat. So heute, als ich ein PDF-Formular in in PDF drucken wollte.
Auf dem Mac gibt es dazu im Druck-Dialog die Möglichkeit, das Dokument als PDF
zu speichern. Funktioniert prima, so lange man nicht versucht, ein Formular
meiner Zentralverwaltung zu drucken. Denn dieses Formular wird ausgefüllt und
anhängig von den Eingaben werden daraus zwei bis fünf neue Dokumente generiert
und diese dann an den Drucker gesendet. Dazu gibt es im Formular extra den <strong>Nur
diesen Button zum Drucken verwenden</strong>-Button. Problem ist nur, dass der so
geöffnete Druck-Dialog es irgendwie nicht schafft, die Ausgabe in ein PDF
umzuleiten. Also begab ich mich auf die Suche nach einem virtuellen PDF-Drucker
für macOS.</p>
<p>Nach gefühlt zweihundert Suchtreffern der Kategorie "Das geht doch direkt aus
dem Druck-Dialog heraus" bin ich dann auf <a class="reference external" href="https://bitbucket.org/codepoet/cups-pdf-for-mac-os-x/wiki/Home">CUPS-PDF</a> und in dessen Dunstkreis auf
<a class="reference external" href="https://onflapp.wordpress.com/vipriser/">VipRiser</a> gestoßen. Da CUPS-PDF wohl den Umweg über PS zu PDF macht, VipRiser
aber <em>native</em> PDFs erstellt, habe ich letzerem den Vorzug gegeben. Die
Installation ist allerdings etwas hakelig:</p>
<ol class="arabic simple">
<li>Das zip-Archiv entpackt und die VipRiser-App nach Applications verschoben</li>
<li>VipRiser gestartet</li>
<li>Nun wird per Dialog hingewiesen, dass der Druckertreiber veraltet sei.
Veraltet ist er eigentlich nicht, eher nicht vorhanden. Aber sei's drum,
Treiber wird installiert.</li>
<li>Nachdem der Treiber installiert wurde, öffnet sich ein Dialog, wie man nun
den Drucker einrichten soll. Dort wird empfohlen, den bei der
Treiberinstallation bereits angelegten Drucker in den Systemeinstellungen
wieder zu löschen und statt dessen einen neuen Drucker anzulegen. Von diesem
Schritt rate ich ab, denn der von mir angelegte Drucker funktionierte nicht.
Nach einer Neuinstallation des Treibers funktionierte aber der während der
Installation erstellte Drucker problemlos.</li>
</ol>
<p>Nun bin ich also in der Lage, das durch seltsame PDF-Formulare deaktivierte
<em>Save as PDF</em> durch die Verwendung eines PDF-Druckers zu umgehen. Nicht schön,
aber es funktioniert.</p></div>Thunderbirds Lightning mit Microsofts Exchange verbindenhttps://blog.pvitt.de/posts/thunderbird-lightning-mit-exchange-verbinden/2019-08-07T11:47:032019-08-07T11:47:03+02:00pvitt<div><p>Mein dienstliches MacBook Pro geht langsam dem Ende seines Nutzungszeitraums
entgegen. Den Lüfter habe ich schon einmal getauscht, nun ist es der Akku, der
immer kurzlebiger wird. Hielt der Laptop 2013 noch gute zehn Stunden mit einer
Akkuladung durch, sind es mittlerweile nur mehr knapp zwei. Das wird auf
Dienstreisen schon mal knapp, wenn nicht gerade eine Steckdose in Reichweite
ist. Das Gerät ist eigentlich noch gut in Schuss, daher wird es demnächst wohl
als Ersatzgerät in meinem Teileschrank landen. Es kommt immer mal vor, das ein
Laptop eines Kollegen eingeschickt werden muss, und mit einem Ersatzgerät mit
zwar eingeschränkter Mobilität lässt sich dennoch fast jede Situation meistern.</p>
<p>Nun bin ich also auf der Suche nach einem neuen Laptop. Ein neues MacBook Pro
könnte es werden, aber angesichts der in der Vergangenheit immer wieder
aufgetretenen Hardware-Problemen und auch der Kosten, die für ein neues Gerät
und damit auch einem neuen Satz Adapter und Docks anfällt, bin ich motiviert,
nach Alternativen zu schauen.</p>
<p>Nach einiger Recherche hat sich herausgestellt, dass bei einem Umstieg auf ein
Linux-Gerät die Nutzung des Exchange-Kalenders einer der größtem Stolpersteine
werden kann. Daher habe ich als Test Thunderbird auf dem Mac mit <a href="https://www.thunderbird.net/en-US/calendar/">Lightning</a>
erweitert. Das ist ein Kalender-Add-On für das Mailprogramm. Damit lässt sich
problemlos mein privater CalDAV-Kalender einbinden. Soweit, so gut.</p>
<h3>ExchangeCalendar</h3>
<p>Dienstlich nutzen wir jedoch den Uni-weiten Exchange Server, und der lässt sich
nicht ohne Weiteres in Lightning einbinden. Für die Nutzung des
Exchange-Kalenders ist daher ein weiteres Add-On nötig, welches den bestechenden
Namen <a href="https://github.com/ExchangeCalendar/exchangecalendar">ExchangeCalendar</a> trägt. Dieses ist nicht über den Add-On-Browser
verfügbar, sondern muss direkt von Github geladen werden. Einmal installiert und
aktiviert, benötigt Thunderbird noch einen Neustart, um die durch das Add-On
hinzugefügten Einstellungen auch anzuzeigen.</p>
<h3>Konfiguration von Unterkalendern</h3>
<p>Wie man einen Kalender einrichtet, ist sehr schön in der <a href="https://github.com/Ericsson/exchangecalendar/wiki/How-to-Add-Your-Calendar-to-Thunderbird">Dokumentation des
Vorgängers</a> von ExchangeCalendar beschrieben, daher will ich auf diesen Punkt
gar nicht weiter eingehen. Viel wichtiger für mich ist aber die Einbindung von
Unterkalendern. Dazu dupliziert man den Exchange Calender in Lightning (via
Kontextmenü) und öffnet die <em>Exchange (EWS) properties</em>. Dort gibt es auf dem
Tab <em>Exchange/Windows (AD)</em> die Option <em>Path below folder base</em>, die man mit
Hilfe des <em>Browse</em>-Buttons ändern kann. Im so geöffnete Dialog lassen sich alle
Unterkalender anzeigen und einer auswählen. Den Schritt muss man also pro
Unterkalender wiederholen.</p>
<p>Kollegen berichteten, dass die Lösung nicht dauerhaft stabil sei, ich habe
bisher aber noch keine Probleme feststellen können. Wenn ich die Entscheidung
für ein neues Gerät gefällt habe und es ein Linux-Gerät wird, werde ich sicher
noch den ein oder anderen Post über den Umstieg schreiben, auch über bis dahin
eventuell aufgetretene Probleme mit den Kalendern.</p>
<p><img src="https://vg06.met.vgwort.de/na/680394bfe57e4caba0172fc180730eff" width="1" height="1" alt=""></p></div><div><p>Mein dienstliches MacBook Pro geht langsam dem Ende seines Nutzungszeitraums
entgegen. Den Lüfter habe ich schon einmal getauscht, nun ist es der Akku, der
immer kurzlebiger wird. Hielt der Laptop 2013 noch gute zehn Stunden mit einer
Akkuladung durch, sind es mittlerweile nur mehr knapp zwei. Das wird auf
Dienstreisen schon mal knapp, wenn nicht gerade eine Steckdose in Reichweite
ist. Das Gerät ist eigentlich noch gut in Schuss, daher wird es demnächst wohl
als Ersatzgerät in meinem Teileschrank landen. Es kommt immer mal vor, das ein
Laptop eines Kollegen eingeschickt werden muss, und mit einem Ersatzgerät mit
zwar eingeschränkter Mobilität lässt sich dennoch fast jede Situation meistern.</p>
<p>Nun bin ich also auf der Suche nach einem neuen Laptop. Ein neues MacBook Pro
könnte es werden, aber angesichts der in der Vergangenheit immer wieder
aufgetretenen Hardware-Problemen und auch der Kosten, die für ein neues Gerät
und damit auch einem neuen Satz Adapter und Docks anfällt, bin ich motiviert,
nach Alternativen zu schauen.</p>
<p>Nach einiger Recherche hat sich herausgestellt, dass bei einem Umstieg auf ein
Linux-Gerät die Nutzung des Exchange-Kalenders einer der größtem Stolpersteine
werden kann. Daher habe ich als Test Thunderbird auf dem Mac mit <a href="https://www.thunderbird.net/en-US/calendar/">Lightning</a>
erweitert. Das ist ein Kalender-Add-On für das Mailprogramm. Damit lässt sich
problemlos mein privater CalDAV-Kalender einbinden. Soweit, so gut.</p>
<h3>ExchangeCalendar</h3>
<p>Dienstlich nutzen wir jedoch den Uni-weiten Exchange Server, und der lässt sich
nicht ohne Weiteres in Lightning einbinden. Für die Nutzung des
Exchange-Kalenders ist daher ein weiteres Add-On nötig, welches den bestechenden
Namen <a href="https://github.com/ExchangeCalendar/exchangecalendar">ExchangeCalendar</a> trägt. Dieses ist nicht über den Add-On-Browser
verfügbar, sondern muss direkt von Github geladen werden. Einmal installiert und
aktiviert, benötigt Thunderbird noch einen Neustart, um die durch das Add-On
hinzugefügten Einstellungen auch anzuzeigen.</p>
<h3>Konfiguration von Unterkalendern</h3>
<p>Wie man einen Kalender einrichtet, ist sehr schön in der <a href="https://github.com/Ericsson/exchangecalendar/wiki/How-to-Add-Your-Calendar-to-Thunderbird">Dokumentation des
Vorgängers</a> von ExchangeCalendar beschrieben, daher will ich auf diesen Punkt
gar nicht weiter eingehen. Viel wichtiger für mich ist aber die Einbindung von
Unterkalendern. Dazu dupliziert man den Exchange Calender in Lightning (via
Kontextmenü) und öffnet die <em>Exchange (EWS) properties</em>. Dort gibt es auf dem
Tab <em>Exchange/Windows (AD)</em> die Option <em>Path below folder base</em>, die man mit
Hilfe des <em>Browse</em>-Buttons ändern kann. Im so geöffnete Dialog lassen sich alle
Unterkalender anzeigen und einer auswählen. Den Schritt muss man also pro
Unterkalender wiederholen.</p>
<p>Kollegen berichteten, dass die Lösung nicht dauerhaft stabil sei, ich habe
bisher aber noch keine Probleme feststellen können. Wenn ich die Entscheidung
für ein neues Gerät gefällt habe und es ein Linux-Gerät wird, werde ich sicher
noch den ein oder anderen Post über den Umstieg schreiben, auch über bis dahin
eventuell aufgetretene Probleme mit den Kalendern.</p>
<p><img src="https://vg06.met.vgwort.de/na/680394bfe57e4caba0172fc180730eff" width="1" height="1" alt=""></p></div>OpenID ist tot! Oder?https://blog.pvitt.de/posts/openid-ist-tot/2018-09-19T17:47:30+02:002018-09-19T17:47:30+02:00pvitt<div><p>Am Anfang war das Internet wüst und leer. Nach und nach kamen aber immer mehr
Seite, Dienste und Anbieter hinzu. Und mit ihnen kamen auch immer mehr
Anmeldedaten zusammen. Als das Problem immer schlimmer wurde, versuchte man mit
unterschiedlichen Methoden ein <a href="https://de.wikipedia.org/wiki/Single_Sign-on">Single Sign-On (SSO)</a> zu etablieren. Eine
dieser Methoden ist <a href="https://de.wikipedia.org/wiki/OpenID">OpenID</a>. </p>
<p>Das Besondere dieser Methode ist, dass man nicht auf einen zentralen Anbieter
angewiesen ist. Jeder kann sich mit geringem Aufwand über seine Webseite einen
OpenID-Provider bauen (der dann allerdings doch nicht ganz ohne einen anderen
Anbieter auskommt), mit etwas mehr Aufwand ist auch ein <a href="https://marvindickhaus.de/2014/04/simpleid-openid-auf-deinem-uberspace/">eigener OpenID-Server</a>
möglich.</p>
<p>Einen solchen OpenID-Server betreibe ich seit geraumer Zeit. Leider musste ich
feststellen, dass es über die Jahre nicht wirklich viele Einsatzmöglichkeiten
für OpenID gab. Heute wurde ich dann plötzlich gewahr, dass auch die Webseiten
des <a href="https://stackexchange.com/">StackExchange-Netzwerks</a> OpenID nicht mehr als Authentifizierungsmethode
akzeptieren. Damit ist für mich der letzte Anwendungsfall für meinen OpenID-Server
weggebrochen. Und auch die letzte Hoffnung, dass es Abseits von Google und
Facebook so etwas wie ein dezentrales SSO geben könnte. Fortschritt ist eben
doch ein Vektor. Nicht nur die Geschwindigkeit ist interessant, auch die
Richtung kann entscheidend sein.</p></div><div><p>Am Anfang war das Internet wüst und leer. Nach und nach kamen aber immer mehr
Seite, Dienste und Anbieter hinzu. Und mit ihnen kamen auch immer mehr
Anmeldedaten zusammen. Als das Problem immer schlimmer wurde, versuchte man mit
unterschiedlichen Methoden ein <a href="https://de.wikipedia.org/wiki/Single_Sign-on">Single Sign-On (SSO)</a> zu etablieren. Eine
dieser Methoden ist <a href="https://de.wikipedia.org/wiki/OpenID">OpenID</a>. </p>
<p>Das Besondere dieser Methode ist, dass man nicht auf einen zentralen Anbieter
angewiesen ist. Jeder kann sich mit geringem Aufwand über seine Webseite einen
OpenID-Provider bauen (der dann allerdings doch nicht ganz ohne einen anderen
Anbieter auskommt), mit etwas mehr Aufwand ist auch ein <a href="https://marvindickhaus.de/2014/04/simpleid-openid-auf-deinem-uberspace/">eigener OpenID-Server</a>
möglich.</p>
<p>Einen solchen OpenID-Server betreibe ich seit geraumer Zeit. Leider musste ich
feststellen, dass es über die Jahre nicht wirklich viele Einsatzmöglichkeiten
für OpenID gab. Heute wurde ich dann plötzlich gewahr, dass auch die Webseiten
des <a href="https://stackexchange.com/">StackExchange-Netzwerks</a> OpenID nicht mehr als Authentifizierungsmethode
akzeptieren. Damit ist für mich der letzte Anwendungsfall für meinen OpenID-Server
weggebrochen. Und auch die letzte Hoffnung, dass es Abseits von Google und
Facebook so etwas wie ein dezentrales SSO geben könnte. Fortschritt ist eben
doch ein Vektor. Nicht nur die Geschwindigkeit ist interessant, auch die
Richtung kann entscheidend sein.</p></div>Wo ist rst2man?https://blog.pvitt.de/posts/wo-ist-rst2man/2018-07-31T11:22:192018-07-31T11:22:19+02:00pvitt<div><p>Der Fortran-Parser von ctags versteht bisher das protected-Attribut nicht, mit
dem der schreibende Zugriff auf eine öffentliche Modulvariable von außerhalb des
Moduls unterbunden werden kann. ctags liegt auf github, also war es kein
Problem, den Parser soweit <a href="https://github.com/universal-ctags/ctags/commit/e65e91a2eca1cd3315eefd23a8fbd1067309b2fc">anzupassen</a>, dass er beim Auftreten des Attributs
zumindest die Variable weiterhin verarbeitet.</p>
<p>Problematisch wurde es allerdings, als ich den Parser lokal gebaut habe. Denn
die manpage war nur sehr übersichtlich. In der Doku findet man dazu den Satz,
dass für die Erstellung der manpage das Python-Tool rst2man benötigt wird. Aber
woher bekommt man es? Es gehört wohl zu den docutils, aber
<code>pip install docutils</code> hat es nicht installiert. Auch <code>port install py-docutils</code>
hab nicht weitergeholfen. Im Netz habe ich dann den Hinweis für
Mercurial-Entwickler gefunden, dass man sich das Sandbox-Archiv des
docutils-Projekts laden soll. Aber auch dort ist es nicht enthalten.
Schlussendlich habe ich das Tool <a href="https://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/tools/rst2man.py">direkt aus dem docutils-Repository</a>
geladen. Dort liegt es unterhalb des Ordners, der via pip installiert wird.
Eventuell kann man das PyPi-Package so anpassen, dass rst2man mitinstalliert
wird, aber ob das gewollt ist und wie das geht, weiß ich gerade nicht. Daher
habe ich den pragmatisch und schnellen Weg gewählt.</p></div><div><p>Der Fortran-Parser von ctags versteht bisher das protected-Attribut nicht, mit
dem der schreibende Zugriff auf eine öffentliche Modulvariable von außerhalb des
Moduls unterbunden werden kann. ctags liegt auf github, also war es kein
Problem, den Parser soweit <a href="https://github.com/universal-ctags/ctags/commit/e65e91a2eca1cd3315eefd23a8fbd1067309b2fc">anzupassen</a>, dass er beim Auftreten des Attributs
zumindest die Variable weiterhin verarbeitet.</p>
<p>Problematisch wurde es allerdings, als ich den Parser lokal gebaut habe. Denn
die manpage war nur sehr übersichtlich. In der Doku findet man dazu den Satz,
dass für die Erstellung der manpage das Python-Tool rst2man benötigt wird. Aber
woher bekommt man es? Es gehört wohl zu den docutils, aber
<code>pip install docutils</code> hat es nicht installiert. Auch <code>port install py-docutils</code>
hab nicht weitergeholfen. Im Netz habe ich dann den Hinweis für
Mercurial-Entwickler gefunden, dass man sich das Sandbox-Archiv des
docutils-Projekts laden soll. Aber auch dort ist es nicht enthalten.
Schlussendlich habe ich das Tool <a href="https://sourceforge.net/p/docutils/code/HEAD/tree/trunk/docutils/tools/rst2man.py">direkt aus dem docutils-Repository</a>
geladen. Dort liegt es unterhalb des Ordners, der via pip installiert wird.
Eventuell kann man das PyPi-Package so anpassen, dass rst2man mitinstalliert
wird, aber ob das gewollt ist und wie das geht, weiß ich gerade nicht. Daher
habe ich den pragmatisch und schnellen Weg gewählt.</p></div>Lazy-load JavaScripthttps://blog.pvitt.de/posts/lazy-load-javascript/2018-04-07T10:16:032018-04-07T10:16:03+02:00pvitt<div><p>Mein kürzlich schon mal erwähntes Blogprojekt basiert, wie diese Seite hier
auch, auf einem Static Site Generator zur Erzeugung des HTML. Hier läuft
<a href="https://getnikola.com/">Nikola</a> im Hintergrund, allerdings bin ich mit einigen Dingen nicht ganz so
zufrieden. Zuvorderst ist mir das Datenvolumen, welches für das Laden einer
Seite transferiert wird, viel zu groß. So werden aktuell beim Aufruf von
<code>blog.pvitt.de</code> 488,04 kB versendet. Davon entfallen aber nur 42,3 kB auf HTML,
dafür aber 136,39 kB auf CSS und sogar 311,13 kB auf JavaScript. Das Problem
kann man sicher über die Erstellung eines Themes angehen, aber Nikola macht es
den Nutzern durch fehlende oder schlechte Dokumentation oder komplexe Strukturen
nicht gerade einfach.</p>
<p>Daher habe ich für das neue Projekt nach einem alternativen Static Site
Generator umgesehen und habe einige ausprobiert. Nach einigen Tests bin ich dann
bei <a href="https://blog.getpelican.com/">Pelican</a> gelandet, der mich bisher auch nicht enttäuscht hat. Nach der
einfachen Installation und der ebenfalls einfachen Installation eines Themes
für die Seite sowie ein wenig Optimierung von CSS und mit Verwendung von SVG als
Image-Format komme ich mit zwei Testposts auf der Startseite aktuell auf ein
Transfervolumen von von 7,13 kB, 3,8 kB HTML, 2,74 kB für CSS und 609 B für SVG.
Mit Transportkomprimierung sind das sogar nur 3,85 kB, die tatsächlich über die
Leitung gehen. Das ist mal eine andere Hausnummer.</p>
<p>Umso ernüchterter war ich, als ich dann die Kommentarfunktion via <a href="https://posativ.org/isso/">isso</a>
nachrüsten wollte. Denn dafür ist ein JavaScript-Paket nötig, welches zwar schon
minimiert wurde, trotzdem aber noch 54,63 kB auf die Waage (und 20,36 kB auf die
Leitung) bringt. Das ist eindeutig zu viel für eine <em>einfache</em>
Kommentarfunktion.</p>
<p>Meine Idee war nun, die Kommentarfunktionalität nur dann zu laden, wenn der
Nutzer diese auch nutzen möchte. Dazu musste ich zwar ein wenig HTML ergänzen,
aber die paar Hundert Byte fallen im Relation zu den 54 kB dann doch nicht
wirklich ins Gewicht.</p>
<p>Der Platzhalter im DOM, an dem das JavaScript seinen HTML-Code ablegt, blieb
unverändert im Code. Aber das Script-Tag aus dem Header habe ich entfernt:</p>
<pre class="code literal-block"><span></span><span class="nt"><script</span> <span class="na">data-isso=</span><span class="s">"//comments.example.tld/"</span>
<span class="na">src=</span><span class="s">"//comments.example.tld/js/embed.min.js"</span><span class="nt">></script></span>
</pre>
<p>Stattdessen wird dieses nun erst auf Benutzerwunsch geladen:</p>
<pre class="code literal-block"><span></span><span class="cp">{%</span> <span class="k">if</span> <span class="nv">ISSO_SITENAME</span> <span class="cp">%}</span><span class="x"></span>
<span class="x"><input id="issoBtn" type="image" height="16" src="/theme/images/comment.svg" onclick="</span>
<span class="x"> var s = document.createElement('script');</span>
<span class="x"> s.type = 'text/javascript';</span>
<span class="x"> s.src = '//</span><span class="cp">{{</span> <span class="nv">ISSO_SITENAME</span> <span class="cp">}}</span><span class="x">/js/embed.min.js';</span>
<span class="x"> document.body.appendChild(s);</span>
<span class="x"> this.style.display = 'none';</span>
<span class="x"> " /></span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre>
<p>Das input-Tag wird (wie auch der Platzhalter) nur eingefügt, wenn ISSO auch
konfiguriert wurde. Es zeigt eine Sprechblase an, mit SVG realisiert nur 184
Byte groß. Wenn der Benutzer auf diese Sprechblase klickt, wird das JavaScript
geladen und angehängt, sodass es aktiv werden kann. Im Anschluss wird noch die
Sprechblase versteckt, da nun die Kommentare ja geladen sind.</p>
<p>Isso verfügt auch noch über die Möglichkeit, das clientseitige JavaScript zu
konfigurieren. Dies geschieht an Hand von data-*-Attributen, die am
Script-Element definiert werden. Da ich das Script-Element erst zur Laufzeit
erzeuge, müssen natürlich auch die Attribute zur Laufzeut erzeugt werden. Dazu
fügen wir diese Zeilen vor dem Aufruf von <code>document.body.append</code> ein:</p>
<pre class="code literal-block"><span></span>var aia = document.createAttribute('data-isso-avatar');
aia.value = 'false';
s.setAttributeNode(aia);
var aiv = document.createAttribute('data-isso-vote');
aiv.value = 'false';
s.setAttributeNode(aiv);
</pre>
<p>Mit dieser Lösung bleibt das Datenvolumen bei einem normalen Pageload klein, und
nur, wenn kommentiert werden soll, wird auch das große JavaScript nachgeladen.
Was mich an dieser Lösung noch stört, sind zwei Dinge:</p>
<ol>
<li>Der Leser sieht nicht, ob es schon Kommentare gibt. Hier überlege ich
aktuell noch, wie ich die Anzahl der bisher schon vorhandenen Kommentare
ermitteln kann, bevor ich das JavaScript geladen habe.</li>
<li>Die Sprechblase auf der Seite wird auch dann versteckt, wenn das Javscript
nicht geladen wurde. Das ist in der Praxis sicher kein wirkliches Problem,
da im Fall, dass das JavaScript nicht geladen werden kann, wahrscheinlich die
Seite an sich oder der Server einen Knacks hat. Da wäre ein Reload der Seite
sicher besser. Aber aus Gründen der Vollständigkeit und weil ich es einfach
gern wissen würde, interessiert mich schon, wie ich den HTTP-Response für
das Script ermitteln kann.</li>
</ol>
<p><img src="https://vg06.met.vgwort.de/na/becd69eaad1a48ae9c4a95f3a2c66bee" width="1" height="1" alt=""></p></div><div><p>Mein kürzlich schon mal erwähntes Blogprojekt basiert, wie diese Seite hier
auch, auf einem Static Site Generator zur Erzeugung des HTML. Hier läuft
<a href="https://getnikola.com/">Nikola</a> im Hintergrund, allerdings bin ich mit einigen Dingen nicht ganz so
zufrieden. Zuvorderst ist mir das Datenvolumen, welches für das Laden einer
Seite transferiert wird, viel zu groß. So werden aktuell beim Aufruf von
<code>blog.pvitt.de</code> 488,04 kB versendet. Davon entfallen aber nur 42,3 kB auf HTML,
dafür aber 136,39 kB auf CSS und sogar 311,13 kB auf JavaScript. Das Problem
kann man sicher über die Erstellung eines Themes angehen, aber Nikola macht es
den Nutzern durch fehlende oder schlechte Dokumentation oder komplexe Strukturen
nicht gerade einfach.</p>
<p>Daher habe ich für das neue Projekt nach einem alternativen Static Site
Generator umgesehen und habe einige ausprobiert. Nach einigen Tests bin ich dann
bei <a href="https://blog.getpelican.com/">Pelican</a> gelandet, der mich bisher auch nicht enttäuscht hat. Nach der
einfachen Installation und der ebenfalls einfachen Installation eines Themes
für die Seite sowie ein wenig Optimierung von CSS und mit Verwendung von SVG als
Image-Format komme ich mit zwei Testposts auf der Startseite aktuell auf ein
Transfervolumen von von 7,13 kB, 3,8 kB HTML, 2,74 kB für CSS und 609 B für SVG.
Mit Transportkomprimierung sind das sogar nur 3,85 kB, die tatsächlich über die
Leitung gehen. Das ist mal eine andere Hausnummer.</p>
<p>Umso ernüchterter war ich, als ich dann die Kommentarfunktion via <a href="https://posativ.org/isso/">isso</a>
nachrüsten wollte. Denn dafür ist ein JavaScript-Paket nötig, welches zwar schon
minimiert wurde, trotzdem aber noch 54,63 kB auf die Waage (und 20,36 kB auf die
Leitung) bringt. Das ist eindeutig zu viel für eine <em>einfache</em>
Kommentarfunktion.</p>
<p>Meine Idee war nun, die Kommentarfunktionalität nur dann zu laden, wenn der
Nutzer diese auch nutzen möchte. Dazu musste ich zwar ein wenig HTML ergänzen,
aber die paar Hundert Byte fallen im Relation zu den 54 kB dann doch nicht
wirklich ins Gewicht.</p>
<p>Der Platzhalter im DOM, an dem das JavaScript seinen HTML-Code ablegt, blieb
unverändert im Code. Aber das Script-Tag aus dem Header habe ich entfernt:</p>
<pre class="code literal-block"><span></span><span class="nt"><script</span> <span class="na">data-isso=</span><span class="s">"//comments.example.tld/"</span>
<span class="na">src=</span><span class="s">"//comments.example.tld/js/embed.min.js"</span><span class="nt">></script></span>
</pre>
<p>Stattdessen wird dieses nun erst auf Benutzerwunsch geladen:</p>
<pre class="code literal-block"><span></span><span class="cp">{%</span> <span class="k">if</span> <span class="nv">ISSO_SITENAME</span> <span class="cp">%}</span><span class="x"></span>
<span class="x"><input id="issoBtn" type="image" height="16" src="/theme/images/comment.svg" onclick="</span>
<span class="x"> var s = document.createElement('script');</span>
<span class="x"> s.type = 'text/javascript';</span>
<span class="x"> s.src = '//</span><span class="cp">{{</span> <span class="nv">ISSO_SITENAME</span> <span class="cp">}}</span><span class="x">/js/embed.min.js';</span>
<span class="x"> document.body.appendChild(s);</span>
<span class="x"> this.style.display = 'none';</span>
<span class="x"> " /></span>
<span class="cp">{%</span> <span class="k">endif</span> <span class="cp">%}</span><span class="x"></span>
</pre>
<p>Das input-Tag wird (wie auch der Platzhalter) nur eingefügt, wenn ISSO auch
konfiguriert wurde. Es zeigt eine Sprechblase an, mit SVG realisiert nur 184
Byte groß. Wenn der Benutzer auf diese Sprechblase klickt, wird das JavaScript
geladen und angehängt, sodass es aktiv werden kann. Im Anschluss wird noch die
Sprechblase versteckt, da nun die Kommentare ja geladen sind.</p>
<p>Isso verfügt auch noch über die Möglichkeit, das clientseitige JavaScript zu
konfigurieren. Dies geschieht an Hand von data-*-Attributen, die am
Script-Element definiert werden. Da ich das Script-Element erst zur Laufzeit
erzeuge, müssen natürlich auch die Attribute zur Laufzeut erzeugt werden. Dazu
fügen wir diese Zeilen vor dem Aufruf von <code>document.body.append</code> ein:</p>
<pre class="code literal-block"><span></span>var aia = document.createAttribute('data-isso-avatar');
aia.value = 'false';
s.setAttributeNode(aia);
var aiv = document.createAttribute('data-isso-vote');
aiv.value = 'false';
s.setAttributeNode(aiv);
</pre>
<p>Mit dieser Lösung bleibt das Datenvolumen bei einem normalen Pageload klein, und
nur, wenn kommentiert werden soll, wird auch das große JavaScript nachgeladen.
Was mich an dieser Lösung noch stört, sind zwei Dinge:</p>
<ol>
<li>Der Leser sieht nicht, ob es schon Kommentare gibt. Hier überlege ich
aktuell noch, wie ich die Anzahl der bisher schon vorhandenen Kommentare
ermitteln kann, bevor ich das JavaScript geladen habe.</li>
<li>Die Sprechblase auf der Seite wird auch dann versteckt, wenn das Javscript
nicht geladen wurde. Das ist in der Praxis sicher kein wirkliches Problem,
da im Fall, dass das JavaScript nicht geladen werden kann, wahrscheinlich die
Seite an sich oder der Server einen Knacks hat. Da wäre ein Reload der Seite
sicher besser. Aber aus Gründen der Vollständigkeit und weil ich es einfach
gern wissen würde, interessiert mich schon, wie ich den HTTP-Response für
das Script ermitteln kann.</li>
</ol>
<p><img src="https://vg06.met.vgwort.de/na/becd69eaad1a48ae9c4a95f3a2c66bee" width="1" height="1" alt=""></p></div>Zeitnotationhttps://blog.pvitt.de/posts/zeitnotation/2018-04-02T11:45:192018-04-02T11:45:19+02:00pvitt<div><p>Die Notation von Zeitpunkten kann schon mal zu Verwirrung führen. Bleibt man in
einem Kulturkreis, ist es noch recht einfach. Das Datum <em>02. April 2018</em> kann
jeder Deutsche lesen, wahrscheinlich auch viele Menschen aus anderen
Kulturkreisen, sofern sie <em>Januar</em> als Monat (er)kennen. Schwieriger wird es
schon, wenn das Datum ohne textuelle Monatsangabe einfach als <em>02.04.2018</em>
notiert wird. Personen aus dem angelsächsischen Raum werden dies wahrscheinlich
als 04. Februar 2018 lesen. Denn sie sind es gewohnt, die Monatsangabe vor die
Tagesangabe zu stellen.</p>
<p>In meiner täglichen Arbeit in einem internationalen Team haben wir gelegentlich
das Problem, dass Datumsangaben nicht sauber notiert und daher nicht eindeutig
erkennbar sind. Zwar haben wir uns auf eine einheitliche Verwendung verständigt,
aber manchmal siegt die Gewohnheit und manchmal legen einem Tools Steine in den
Weg, weil sie nicht in der Lage sind, das Datum nach unseren Wünschen zu
notieren.</p>
<p>Aber wie sieht nun unser gemeinsamer Standard aus, der für Kulturgruppen
weltweit lesbar ist? Nun, es gibt mit der <a href="https://de.wikipedia.org/wiki/ISO_8601">ISO 8601</a> tatsächlich seit
längerer Zeit eine ISO-Norm für Datumsangaben. Diese Norm wurde durch die
Übernahme in die europäische Norm EN 28601:1992 auch für Deutschland gültig und
nennt sich hierzulande ISO 8601:1988.</p>
<p>Mir als Softwerker ist dieses Format schon länger ein Begriff, aber eine
Verwendung im Alltag habe ich nie bewusst bemerkt. Daher war ich umso
überraschter, als ich in einer sehr guten <a href="http://www.cl.cam.ac.uk/~mgk25/iso-time.html">Zusammenfassung des Formats von
Markus Kuhn</a> las, dass dieses Format das Standardformat in Deutschland sei.</p>
<p>In der Wikipedia liest man nun dazu:</p>
<blockquote>
<p>Darüber hinaus ist das Datumsformat nach DIN EN 28601 in Deutschland am 1. Mai
1996 zum einzigen normgerechten numerischen Datumsformat (z. B. 1996-05-01)
erhoben worden und löste damit das traditionelle Format nach DIN 1355-1
(1.5.1996) ab. Alle Einrichtungen, die im Einflussbereich der DIN-Normen
stehen (so auch alle Bildungseinrichtungen und öffentliche Einrichtungen),
sind zur Benutzung des neuen Formats angehalten. Allerdings verwendeten große
Teile der Bevölkerung im Alltag weiterhin das alte Format, was durch die
Neuregelung der DIN 5008 im Jahr 2001 zur Wiederzulassung des üblichen Formats
führte, wenn damit keine Missverständnisse entstehen.</p>
</blockquote>
<p>Um es meinem in vielen Bereich durch internationale Kontakte geprägten Umfeld
zu erleichtern, werde ich daher nur noch das Datum nach ISO8601 verwenden.</p>
<p><img src="https://vg06.met.vgwort.de/na/5afd492eacba474c8345c7890baf2e48" width="1" height="1" alt=""></p></div><div><p>Die Notation von Zeitpunkten kann schon mal zu Verwirrung führen. Bleibt man in
einem Kulturkreis, ist es noch recht einfach. Das Datum <em>02. April 2018</em> kann
jeder Deutsche lesen, wahrscheinlich auch viele Menschen aus anderen
Kulturkreisen, sofern sie <em>Januar</em> als Monat (er)kennen. Schwieriger wird es
schon, wenn das Datum ohne textuelle Monatsangabe einfach als <em>02.04.2018</em>
notiert wird. Personen aus dem angelsächsischen Raum werden dies wahrscheinlich
als 04. Februar 2018 lesen. Denn sie sind es gewohnt, die Monatsangabe vor die
Tagesangabe zu stellen.</p>
<p>In meiner täglichen Arbeit in einem internationalen Team haben wir gelegentlich
das Problem, dass Datumsangaben nicht sauber notiert und daher nicht eindeutig
erkennbar sind. Zwar haben wir uns auf eine einheitliche Verwendung verständigt,
aber manchmal siegt die Gewohnheit und manchmal legen einem Tools Steine in den
Weg, weil sie nicht in der Lage sind, das Datum nach unseren Wünschen zu
notieren.</p>
<p>Aber wie sieht nun unser gemeinsamer Standard aus, der für Kulturgruppen
weltweit lesbar ist? Nun, es gibt mit der <a href="https://de.wikipedia.org/wiki/ISO_8601">ISO 8601</a> tatsächlich seit
längerer Zeit eine ISO-Norm für Datumsangaben. Diese Norm wurde durch die
Übernahme in die europäische Norm EN 28601:1992 auch für Deutschland gültig und
nennt sich hierzulande ISO 8601:1988.</p>
<p>Mir als Softwerker ist dieses Format schon länger ein Begriff, aber eine
Verwendung im Alltag habe ich nie bewusst bemerkt. Daher war ich umso
überraschter, als ich in einer sehr guten <a href="http://www.cl.cam.ac.uk/~mgk25/iso-time.html">Zusammenfassung des Formats von
Markus Kuhn</a> las, dass dieses Format das Standardformat in Deutschland sei.</p>
<p>In der Wikipedia liest man nun dazu:</p>
<blockquote>
<p>Darüber hinaus ist das Datumsformat nach DIN EN 28601 in Deutschland am 1. Mai
1996 zum einzigen normgerechten numerischen Datumsformat (z. B. 1996-05-01)
erhoben worden und löste damit das traditionelle Format nach DIN 1355-1
(1.5.1996) ab. Alle Einrichtungen, die im Einflussbereich der DIN-Normen
stehen (so auch alle Bildungseinrichtungen und öffentliche Einrichtungen),
sind zur Benutzung des neuen Formats angehalten. Allerdings verwendeten große
Teile der Bevölkerung im Alltag weiterhin das alte Format, was durch die
Neuregelung der DIN 5008 im Jahr 2001 zur Wiederzulassung des üblichen Formats
führte, wenn damit keine Missverständnisse entstehen.</p>
</blockquote>
<p>Um es meinem in vielen Bereich durch internationale Kontakte geprägten Umfeld
zu erleichtern, werde ich daher nur noch das Datum nach ISO8601 verwenden.</p>
<p><img src="https://vg06.met.vgwort.de/na/5afd492eacba474c8345c7890baf2e48" width="1" height="1" alt=""></p></div>Alternierende Navigationselemente mit CSShttps://blog.pvitt.de/posts/alternierende-navigationselemente-css/2018-04-01T00:04:002018-04-01T00:04:00+02:00pvitt<div><p>Bei einem anderen Blogprojekte verwende ich ein Theme, das auf einem
zweispaltiegen Layout aufbaut. Das Theme ist responsiv, es nimmt also
abhängig von der Größe des Darstellungsfensters eine andere Struktur an, in
meinem Fall bei schmalem Screen ein einspaltiges Layout statt eines
zweispaltigen.
Nun ist es so, dass im zweispaltigen Layout im Menübereich jeweils eine Liste
mit Kategorien und Tags anzeigt werden, die ich im einspaltigen Layout wegen
ihrer Position über dem Inhalt nicht mehr anzeigen möchte. Andernfalls müsste
der User zu lange scrollen, um beim eigentlichen Inhalt anzukommen.</p>
<p>Nach etwas ratlosem Rumprobieren und CSS-Doku lesen bin ich über das
<a href="https://www.w3.org/TR/CSS1/#display">display</a>-Property gestolpert. Damit konnte ich dann etwas umständlich, da
ich die Klasse sowohl an der Überschrift als auch an der Liste anhängen musste,
die längeren Teile verstecken:</p>
<pre class="code literal-block"><span></span><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">max-width</span><span class="o">:</span> <span class="nt">800px</span><span class="o">)</span> <span class="p">{</span>
<span class="p">.</span><span class="nc">horizontal</span> <span class="p">{</span> <span class="k">display</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</pre>
<pre class="code literal-block"><span></span>{% if DISPLAY_CATEGORIES_ON_MENU and categories %}
<span class="p"><</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">"horizontal"</span><span class="p">></span>Kategorien<span class="p"></</span><span class="nt">h2</span><span class="p">></span>
<span class="p"><</span><span class="nt">ul</span> <span class="na">class</span><span class="o">=</span><span class="s">"navbar horizontal"</span><span class="p">></span>
{% for cat, null in categories %}
<span class="p"><</span><span class="nt">li</span><span class="err">{%</span> <span class="na">if</span> <span class="na">cat </span><span class="o">=</span><span class="s">=</span> <span class="na">category</span> <span class="err">%}</span> <span class="na">class</span><span class="o">=</span><span class="s">"active"</span><span class="err">{%</span> <span class="na">endif</span> <span class="err">%}</span><span class="p">></span>
<span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"{{ SITEURL }}/{{ cat.url }}"</span><span class="p">></span>{{ cat }}<span class="p"></</span><span class="nt">a</span><span class="p">></span>
<span class="p"></</span><span class="nt">li</span><span class="p">></span>
{% endfor %}
<span class="p"></</span><span class="nt">ul</span><span class="p">></span>
{% endif %}
</pre>
<p><code>display: none</code> ist in diesem Fall <code>visibility: hidden</code> vorzuziehen, da so
vermieden wird, dass die versteckten Elemente Platz im Layout belegen. Siehe
dazu auch den Abschnitt <em>Hide an Element - display:none or visibility:hidden</em>
auf der <a href="https://www.w3schools.com/css/css_display_visibility.asp">Seite der W3school</a>.</p>
<p>Als das Verstecken funktioniert hat, kam auch schnell die Idee auf, im
einspaltigen Layout etwas Anderes anzeigen zu lassen. Daher habe ich ein
inverses media-Tag erstellt und damit die Menüeinträge dekoriert:</p>
<pre class="code literal-block"><span></span><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">min-width</span><span class="o">:</span> <span class="nt">801px</span><span class="o">)</span> <span class="p">{</span>
<span class="p">.</span><span class="nc">vertical</span> <span class="p">{</span> <span class="k">display</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</pre>
<pre class="code literal-block"><span></span><span class="p"><</span><span class="nt">nav</span> <span class="na">class</span><span class="o">=</span><span class="s">"nav"</span><span class="p">></span>
<span class="p"><</span><span class="nt">ul</span> <span class="na">class</span><span class="o">=</span><span class="s">"list-bare"</span><span class="p">></span>
{% for title, link in MENUITEMS %}
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">"nav__link"</span> <span class="na">href</span><span class="o">=</span><span class="s">"{{ link }}"</span><span class="p">></span>{{ title }}<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
{% endfor %}
{% if DISPLAY_PAGES_ON_MENU and pages %}{% for p in pages %}
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">"nav__link"</span> <span class="na">href</span><span class="o">=</span><span class="s">"{{ SITEURL }}/{{ p.url }}"</span><span class="p">></span>{{ p.title }}<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
{% endfor %}{% endif %}
{% if DISPLAY_CATEGORIES_ON_MENU and categories %}
<span class="p"><</span><span class="nt">li</span> <span class="na">class</span><span class="o">=</span><span class="s">"vertical"</span><span class="p">><</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">"nav__link"</span> <span class="na">href</span><span class="o">=</span><span class="s">"/categories.html"</span><span class="p">></span>Kategorien<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
{% endif %}
{% if DISPLAY_TAGS_ON_MENU and tags %}
<span class="p"><</span><span class="nt">li</span> <span class="na">class</span><span class="o">=</span><span class="s">"vertical"</span><span class="p">><</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">"nav__link"</span> <span class="na">href</span><span class="o">=</span><span class="s">"/tags.html"</span><span class="p">></span>Tags<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
{% endif %}
<span class="p"></</span><span class="nt">ul</span><span class="p">></span>
<span class="p"></</span><span class="nt">nav</span><span class="p">></span>
</pre>
<p>Funktioniert prima. Ich bin mir zwar nicht sicher, ob das korrektes und
sinnvolles CSS ist und ob man dies eigentlich anders macht, daher bin ich
über Hinweise immer dankbar. Was mich noch ein wenig ärgert ist die doppelte
Verwendung der horizontal-Klasse für Überschrift und Liste. Ich muss noch mal
tiefer in die CSS-Doku eintauchen um zu sehen, ob man dies nicht auch eleganter
lösen kann.</p>
<p><img src="https://vg06.met.vgwort.de/na/399916c48510491cb04ab0f184da14fe" width="1" height="1" alt=""></p></div><div><p>Bei einem anderen Blogprojekte verwende ich ein Theme, das auf einem
zweispaltiegen Layout aufbaut. Das Theme ist responsiv, es nimmt also
abhängig von der Größe des Darstellungsfensters eine andere Struktur an, in
meinem Fall bei schmalem Screen ein einspaltiges Layout statt eines
zweispaltigen.
Nun ist es so, dass im zweispaltigen Layout im Menübereich jeweils eine Liste
mit Kategorien und Tags anzeigt werden, die ich im einspaltigen Layout wegen
ihrer Position über dem Inhalt nicht mehr anzeigen möchte. Andernfalls müsste
der User zu lange scrollen, um beim eigentlichen Inhalt anzukommen.</p>
<p>Nach etwas ratlosem Rumprobieren und CSS-Doku lesen bin ich über das
<a href="https://www.w3.org/TR/CSS1/#display">display</a>-Property gestolpert. Damit konnte ich dann etwas umständlich, da
ich die Klasse sowohl an der Überschrift als auch an der Liste anhängen musste,
die längeren Teile verstecken:</p>
<pre class="code literal-block"><span></span><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">max-width</span><span class="o">:</span> <span class="nt">800px</span><span class="o">)</span> <span class="p">{</span>
<span class="p">.</span><span class="nc">horizontal</span> <span class="p">{</span> <span class="k">display</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</pre>
<pre class="code literal-block"><span></span>{% if DISPLAY_CATEGORIES_ON_MENU and categories %}
<span class="p"><</span><span class="nt">h2</span> <span class="na">class</span><span class="o">=</span><span class="s">"horizontal"</span><span class="p">></span>Kategorien<span class="p"></</span><span class="nt">h2</span><span class="p">></span>
<span class="p"><</span><span class="nt">ul</span> <span class="na">class</span><span class="o">=</span><span class="s">"navbar horizontal"</span><span class="p">></span>
{% for cat, null in categories %}
<span class="p"><</span><span class="nt">li</span><span class="err">{%</span> <span class="na">if</span> <span class="na">cat </span><span class="o">=</span><span class="s">=</span> <span class="na">category</span> <span class="err">%}</span> <span class="na">class</span><span class="o">=</span><span class="s">"active"</span><span class="err">{%</span> <span class="na">endif</span> <span class="err">%}</span><span class="p">></span>
<span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="o">=</span><span class="s">"{{ SITEURL }}/{{ cat.url }}"</span><span class="p">></span>{{ cat }}<span class="p"></</span><span class="nt">a</span><span class="p">></span>
<span class="p"></</span><span class="nt">li</span><span class="p">></span>
{% endfor %}
<span class="p"></</span><span class="nt">ul</span><span class="p">></span>
{% endif %}
</pre>
<p><code>display: none</code> ist in diesem Fall <code>visibility: hidden</code> vorzuziehen, da so
vermieden wird, dass die versteckten Elemente Platz im Layout belegen. Siehe
dazu auch den Abschnitt <em>Hide an Element - display:none or visibility:hidden</em>
auf der <a href="https://www.w3schools.com/css/css_display_visibility.asp">Seite der W3school</a>.</p>
<p>Als das Verstecken funktioniert hat, kam auch schnell die Idee auf, im
einspaltigen Layout etwas Anderes anzeigen zu lassen. Daher habe ich ein
inverses media-Tag erstellt und damit die Menüeinträge dekoriert:</p>
<pre class="code literal-block"><span></span><span class="p">@</span><span class="k">media</span> <span class="o">(</span><span class="nt">min-width</span><span class="o">:</span> <span class="nt">801px</span><span class="o">)</span> <span class="p">{</span>
<span class="p">.</span><span class="nc">vertical</span> <span class="p">{</span> <span class="k">display</span><span class="p">:</span> <span class="kc">none</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
</pre>
<pre class="code literal-block"><span></span><span class="p"><</span><span class="nt">nav</span> <span class="na">class</span><span class="o">=</span><span class="s">"nav"</span><span class="p">></span>
<span class="p"><</span><span class="nt">ul</span> <span class="na">class</span><span class="o">=</span><span class="s">"list-bare"</span><span class="p">></span>
{% for title, link in MENUITEMS %}
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">"nav__link"</span> <span class="na">href</span><span class="o">=</span><span class="s">"{{ link }}"</span><span class="p">></span>{{ title }}<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
{% endfor %}
{% if DISPLAY_PAGES_ON_MENU and pages %}{% for p in pages %}
<span class="p"><</span><span class="nt">li</span><span class="p">><</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">"nav__link"</span> <span class="na">href</span><span class="o">=</span><span class="s">"{{ SITEURL }}/{{ p.url }}"</span><span class="p">></span>{{ p.title }}<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
{% endfor %}{% endif %}
{% if DISPLAY_CATEGORIES_ON_MENU and categories %}
<span class="p"><</span><span class="nt">li</span> <span class="na">class</span><span class="o">=</span><span class="s">"vertical"</span><span class="p">><</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">"nav__link"</span> <span class="na">href</span><span class="o">=</span><span class="s">"/categories.html"</span><span class="p">></span>Kategorien<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
{% endif %}
{% if DISPLAY_TAGS_ON_MENU and tags %}
<span class="p"><</span><span class="nt">li</span> <span class="na">class</span><span class="o">=</span><span class="s">"vertical"</span><span class="p">><</span><span class="nt">a</span> <span class="na">class</span><span class="o">=</span><span class="s">"nav__link"</span> <span class="na">href</span><span class="o">=</span><span class="s">"/tags.html"</span><span class="p">></span>Tags<span class="p"></</span><span class="nt">a</span><span class="p">></</span><span class="nt">li</span><span class="p">></span>
{% endif %}
<span class="p"></</span><span class="nt">ul</span><span class="p">></span>
<span class="p"></</span><span class="nt">nav</span><span class="p">></span>
</pre>
<p>Funktioniert prima. Ich bin mir zwar nicht sicher, ob das korrektes und
sinnvolles CSS ist und ob man dies eigentlich anders macht, daher bin ich
über Hinweise immer dankbar. Was mich noch ein wenig ärgert ist die doppelte
Verwendung der horizontal-Klasse für Überschrift und Liste. Ich muss noch mal
tiefer in die CSS-Doku eintauchen um zu sehen, ob man dies nicht auch eleganter
lösen kann.</p>
<p><img src="https://vg06.met.vgwort.de/na/399916c48510491cb04ab0f184da14fe" width="1" height="1" alt=""></p></div>Grundig disqualifiziert sich selbsthttps://blog.pvitt.de/posts/grundig-disqualifiziert-sich-selbst/2017-09-03T21:56:30+02:002017-09-03T21:56:30+02:00pvitt<div><p>Vor einiger Zeit habe ich ein Gerät von Grundig gekauft, welches mit einem Akku
ausgestattet ist. Nun hat der Akku nach erstaunlich wenigen Ladezyklen die
Flügel gestreckt. Das ist zwar ärgerlich, aber sicherlich verschmerzbar, denn
man bekommt heutzutage ja fast an jeder Straßenecke die exotischsten Akkus
angeboten. Leider lassen sich von den sechs Schrauben im Gehäuse nur fünf
entfernen, da die sechste einen Innendreikant aufweist, für den wohl die
wenigsten ein passendes Werkzeug vorrätig halten.</p>
<p>Liebe Firma Grundig: Warum? Ich kann es ja verstehen, dass man verschiedene
Sollbruchstellen einbaut, um z.B. Sicherheitsbestimmungen zu erfüllen. Aber was
überhaupt nicht geht, ist es, zu verhindern, dass Verschleißteile, zu denen der
Akku offensichtlich gehört, getauscht werden können. Statt eines neuen Akkus
muss ich mir nun ein neues Gerät kaufen. Aber, liebe Firma Grundig, seien sie
versichert, dass das neue Gerät nicht aus ihrem Hause stammen wird.</p></div><div><p>Vor einiger Zeit habe ich ein Gerät von Grundig gekauft, welches mit einem Akku
ausgestattet ist. Nun hat der Akku nach erstaunlich wenigen Ladezyklen die
Flügel gestreckt. Das ist zwar ärgerlich, aber sicherlich verschmerzbar, denn
man bekommt heutzutage ja fast an jeder Straßenecke die exotischsten Akkus
angeboten. Leider lassen sich von den sechs Schrauben im Gehäuse nur fünf
entfernen, da die sechste einen Innendreikant aufweist, für den wohl die
wenigsten ein passendes Werkzeug vorrätig halten.</p>
<p>Liebe Firma Grundig: Warum? Ich kann es ja verstehen, dass man verschiedene
Sollbruchstellen einbaut, um z.B. Sicherheitsbestimmungen zu erfüllen. Aber was
überhaupt nicht geht, ist es, zu verhindern, dass Verschleißteile, zu denen der
Akku offensichtlich gehört, getauscht werden können. Statt eines neuen Akkus
muss ich mir nun ein neues Gerät kaufen. Aber, liebe Firma Grundig, seien sie
versichert, dass das neue Gerät nicht aus ihrem Hause stammen wird.</p></div>Kein Web-Interface nach Netgear NAS Updatehttps://blog.pvitt.de/posts/kein-web-interface-nach-netgear-nas-update/2017-08-11T14:59:30+02:002017-08-11T14:59:30+02:00pvitt<div><p>Aus Gründen habe ich mich in das Web-Interface meines Netgear ReadyNAS
eingeloggt. Direkt nach dem Login zeigte es mir eine Benachrichtigung über eine
neue Version des Betriebssystems, welche ich direkt installieren ließ. Leider
war nach dem Update das Web-Interface nicht mehr verfügbar. Er zeigt zwar den
Netgear-Splashscreen, und auch die Authentifizierung erfolgte, aber
anschließend wurde die Admin-Oberfläche nicht gelanden. Stattdessen wurde eine
Fehlerseite angezeigt, die auf eine nicht hilfreiche Supportseite verlinkte.</p>
<p>Da die Kiste prinzipiell ja erreichbar war und sich auch Shares mounten ließen,
konnte es eigentlich keine große Sache sein. Also per ssh verbinden und mal im
Log schauen. Stellt sich raus, dass der apache über das php-Modul stolpert:</p>
<blockquote>
<p>apache2[2555]: apache2: Syntax error on line 244 of /etc/apache2/apache2.conf: Syntax error on line 1 of /etc/apache2/mods-enabled/php5.load: Cannot load /usr/lib/apache2/modules/libphp5.so into server: /usr/lib/apache2/modules/libphp5.so: cannot open shared object file: No such file or directory</p>
<p>systemd[1]: apache2.service: Control process exited, code=exited statAus=1</p>
</blockquote>
<p>Netterweise reicht es, <code>/etc/apache2/apache2.conf</code> und <code>/etc/apache2/apache2.load</code>
zu löschen, um dem apache wieder auf die Füße zu helfen. Nun halt ohne PHP, aber
das scheint nicht benötigt zu werden.</p></div><div><p>Aus Gründen habe ich mich in das Web-Interface meines Netgear ReadyNAS
eingeloggt. Direkt nach dem Login zeigte es mir eine Benachrichtigung über eine
neue Version des Betriebssystems, welche ich direkt installieren ließ. Leider
war nach dem Update das Web-Interface nicht mehr verfügbar. Er zeigt zwar den
Netgear-Splashscreen, und auch die Authentifizierung erfolgte, aber
anschließend wurde die Admin-Oberfläche nicht gelanden. Stattdessen wurde eine
Fehlerseite angezeigt, die auf eine nicht hilfreiche Supportseite verlinkte.</p>
<p>Da die Kiste prinzipiell ja erreichbar war und sich auch Shares mounten ließen,
konnte es eigentlich keine große Sache sein. Also per ssh verbinden und mal im
Log schauen. Stellt sich raus, dass der apache über das php-Modul stolpert:</p>
<blockquote>
<p>apache2[2555]: apache2: Syntax error on line 244 of /etc/apache2/apache2.conf: Syntax error on line 1 of /etc/apache2/mods-enabled/php5.load: Cannot load /usr/lib/apache2/modules/libphp5.so into server: /usr/lib/apache2/modules/libphp5.so: cannot open shared object file: No such file or directory</p>
<p>systemd[1]: apache2.service: Control process exited, code=exited statAus=1</p>
</blockquote>
<p>Netterweise reicht es, <code>/etc/apache2/apache2.conf</code> und <code>/etc/apache2/apache2.load</code>
zu löschen, um dem apache wieder auf die Füße zu helfen. Nun halt ohne PHP, aber
das scheint nicht benötigt zu werden.</p></div>Python iterator in nested typeshttps://blog.pvitt.de/posts/python-iterator-in-nested-types/2017-07-07T12:18:30+02:002017-07-07T12:18:30+02:00pvitt<div><p>Will man in Python einen Iterator für eine Container-Klasse erstellen, deren
Elemente wiederum Instanzen der Container-Klasse sind, so kann dieser Iterator
einfach so erstellt werden:</p>
<pre class="code literal-block"><span></span><span class="k">class</span> <span class="nc">Container</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">for</span> <span class="n">element</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">element</span><span class="p">,</span> <span class="n">Container</span><span class="p">):</span>
<span class="k">for</span> <span class="n">subelement</span> <span class="ow">in</span> <span class="n">element</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">subelement</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">element</span>
</pre>
<p>As simple as that!</p></div><div><p>Will man in Python einen Iterator für eine Container-Klasse erstellen, deren
Elemente wiederum Instanzen der Container-Klasse sind, so kann dieser Iterator
einfach so erstellt werden:</p>
<pre class="code literal-block"><span></span><span class="k">class</span> <span class="nc">Container</span><span class="p">:</span>
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">list</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">def</span> <span class="fm">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">for</span> <span class="n">element</span> <span class="ow">in</span> <span class="bp">self</span><span class="o">.</span><span class="n">list</span><span class="p">:</span>
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">element</span><span class="p">,</span> <span class="n">Container</span><span class="p">):</span>
<span class="k">for</span> <span class="n">subelement</span> <span class="ow">in</span> <span class="n">element</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">subelement</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">yield</span> <span class="n">element</span>
</pre>
<p>As simple as that!</p></div>