Wie bei den meisten anderen Diensten die ich zu Hause betreibe gibt es ein paar Vorbereitungen zu treffen um die Umgebung herzurichten.
Damit Gitea die ganzen Daten von und über meinen Code speichern benötigt es eine Datenbank. Ich habe mich für PostgreSQL entschieden. Das Packet ist auch schnell installiert:
sudo apt-get update
sudo apt-get install -y postgresql postgresql-client
Anschließend empfehle ich noch eine Anpassung der Konfiguration um das völlig veraltete Hashverfahren MD5 durch was moderneres zu ersetzen. Dazu lege ich die Datei /etc/postgres/13/main/conf.d/ an und füge darin die Zeile password_encryption = scram-sha-256
ein.
Die eigentliche Softwareinstallation von Gitea beginnt dann mit dem anlegen des Benutzers gitea:
sudo adduser --system --home /opt/gitea --disabled-login --disabled-password --group gitea
Gitea stellt zwar das Webinterface bereit, die eigentliche Verwaltung des Codes übernimmt aber Git. Daher muss Git natürlich auch installiert sein:
sudo apt-get install -y git
Anschließend komme ich schon zum Download. In diesem Schritt inkludiert, prüfe ich auch die Signatur damit ich keine korrupten oder manipulierten Binaries installiere:
sudo wget -P /opt/gitea/ https://dl.gitea.io/gitea/1.16.8/gitea-1.16.8-linux-amd64
sudo wget -P /opt/gitea/ https://dl.gitea.io/gitea/1.16.8/gitea-1.16.8-linux-amd64.asc
sudo gpg --keyserver keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
sudo gpg --verify /opt/gitea/gitea-1.16.8-linux-amd64.asc /opt/gitea/gitea-1.16.8-linux-amd64
Wenn die Signaturprüfung ein positives Ergebnis hervorbringt kann es weitergehen mit dem anlegen von verschiedenen Ordnern und dem vergeben der passenden Berechtigungen:
sudo mkdir -p /etc/gitea /var/lib/gitea/{custom,data,log}
sudo chown -R gitea:gitea /var/lib/gitea/
sudo chmod -R 750 /var/lib/gitea/
sudo chown root:gitea /etc/gitea
sudo chmod 770 /etc/gitea
Zu beginn habe ich zwar PostgreSQL installiert aber die eigentliche Datenbank innerhalb des Datenbankservers fehlt noch. Der folgende etwas längliche Befehl übernimmt mehrere Aufgaben auf einmal. Zuerst einmal meldet er sich mit dem Datenbankadmin an der Datenbank an. Im Anschluss wird dann der Datenbankbenutzer gitea erzeugt (bitte ein eigenes Passwort anstelle von $pwd einsetzen), eine Datenbank giteadb angelegt und dann der User aus dem vorherigen Schritt mit allen Rechten auf dieser Datenbank versehen.
sudo -u postgres psql -p 5432 --command "CREATE USER gitea WITH PASSWORD '$pwd';" --command "CREATE DATABASE giteadb OWNER gitea ENCODING UTF8 LC_COLLATE 'de_DE.UTF-8' LC_CTYPE 'de_DE.UTF-8';" --command "GRANT ALL PRIVILEGES ON DATABASE giteadb to gitea" postgres
Gitea ist nach der Installation erreichbar unter http://localhost:3000. Das ist natürlich nicht so hilfreich um vom eigenen Arbeitsrechner aus zuzugreifen. Darum schalte ich Nginx als HTTP-Server davor, der kann dann auch gleich eine HTTPS-Verbindung ermöglichen. Dazu ist eine neu Datei /etc/nginx/sites-available/gitea.local mit dem folgenden Inhalt nötig:
server {
listen 192.0.2.10:80;
listen [fc00::10]:80;
return 301 https://$host$request_uri;
}
server {
listen 192.0.2.10:443 ssl http2;
listen [fc00::10]:443 ssl http2;
ssl_certificate /etc/nginx/ssl/gitea.local.cert.chain.pem;
ssl_certificate_key /etc/nginx/ssl/gitea.local.key.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
ssl_protocols TLSv1.3;
ssl_prefer_server_ciphers off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
server_tokens off;
client_max_body_size 200M;
location / {
proxy_pass http://localhost:3000;
}
error_log /var/log/nginx/gitea_error.log;
access_log /var/log/nginx/gitea_access.log;
}
Die IPv4- und IPv6-Adressen, genauso wie die SSL-Zertifikatsdateien müssen natürlich zu den eigenen Gegebenheiten passend ersetzt werden.
Anschließend muss ich die Seite noch aktivieren:
sudo ln -s /etc/nginx/sites-available/gitea.local /etc/nginx/sites-enabled/gitea.local
Eine Sache, die dabei besonders wichtig ist, versteckt sich in der Zeile die mit client_max_body_size
beginnt. Dort wird nämlich festgelegt wie groß die Dateien in eurem Code maximal sein dürfen damit Nginx sie nicht ablehnt. In meinem Beispiel sind es großzügig dimensionierte 200 Megabyte.
Um Gitea bei jedem Neustart des Computers nicht wieder manuell starten zu müssen bietet es sich an, einen Dienst einzurichten. Dazu braucht es wiederum eine neue Datei /etc/systemd/system/gitea.service mit folgendem Inhalt:
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target
After=postgresql.service
[Service]
LimitMEMLOCK=infinity
LimitNOFILE=65535
RestartSec=2s
Type=simple
User=gitea
Group=gitea
WorkingDirectory=/var/lib/gitea/
ExecStart=/usr/local/bin/gitea web -c /etc/gitea/app.ini
Restart=always
Environment=USER=gitea HOME=/opt/gitea GITEA_WORK_DIR=/var/lib/gitea
[Install]
WantedBy=multi-user.target
Aktivieren muss ich auch diese Datei dann wieder und kann bei der Gelegenheit auch alles andere einmal durchstarten:
sudo systemctl daemon-reload
sudo systemctl enable --now gitea
sudo systemctl restart networking
sudo systemctl restart nginx.service
Wenn alles gut gelaufen ist sollte Gitea jetzt unter dem Hostnamen oder der IP aufrufbar sein. Aufrufbar ist aber nicht gleich nutzbar, zu erst muss noch die Grundkonfiguration auf der geöffneten Webseite vorgenommen werden. Insbesondere ist es nötig die Datenbankverbindungsparameter anzugeben. Anschließend können Benutzer und Repositories angelegt werden.
Ich beschränke dann noch die Schreibrechte auf die jetzt entstandene Konfigurationsdatei auf ein Minimum. Sicher ist sicher.
sudo chmod 750 /etc/gitea
sudo chmod 640 /etc/gitea/app.ini
Für das Backup ist es wichtig die Datenbank zu sichern. Das erreiche ich über ein kleines Script:
#!/bin/bash
echo "Init target folder..."
if [ ! -d "/backup/postgres" ]; then
mkdir /backup/postgres
chown postgres:postgres /backup/postgres
chmod 770 /backup/postgres
fi
echo "Create a dump of the database giteadb"
cd /var/lib/postgresql
sudo -u postgres pg_dump --file /backup/postgres/gitea.dump giteadb
Dieses Script rufe ich dann in meinem eigentlich Backup mit Dirvish auf.
Das wiederherstellen der Datenbank aus dem zuvor erstellten Dump ist auch mit wenigen Befehlen getan:
sudo -u postgres psql --command "DROP DATABASE IF EXISTS giteadb;" --command "CREATE DATABASE giteadb OWNER gitea;" --command "GRANT ALL PRIVILEGES ON DATABASE giteadb to gitea;" postgres
sudo -u postgres psql --set ON_ERROR_STOP=on giteadb < /backup/postgres/giteadb.dump
Ich musste noch kein Update machen aber es sollte sich auf den Austausch der Binärdatei beschränken. Aber die Lektüre der Dokumentation ist sicher angebracht.
Bevor man jetzt auf dem Entwicklungsrechner loslegen kann… Ich sagte ja oben das ich selbst ausgestellte Zertifikate nutzte. Damit die von der Git-Client akzeptiert werden muss man sie natürlich in eine Vertrauensstellung bringen.
Unter Debian hat mir der Artikel von Grumpy Techie dabei geholfen.
Unter Windows ist es etwas irritierend. Git benutzt standardmäßig nicht den Zertifikatsspeicher von Windows sondern einen Eigenen. Dabei handelt es sich um eine Textdatei deren Pfad man mit dem Befehl git config --list
herausfinden kann. Die Zeile die mit http.sslcainfo beginnt gibt den Speicherort an. In diese Datei kann man die eigenen Zertifikate einfach anfügen. Anschließend kann der Spaß beginnen.
Weil ich das Thema aber für sensibel erachte und ich einfach zu neugierig bin, habe ich auch gleich mal eine oberflächliche Prüfung der Webseiten auf Datenschutzaspekte vorgenommen. Ich bin da kein Profi also erwartet nicht zu viel. Mein Vorgehen ist recht einfach: mal schauen, was der Browser in den Entwicklungstools so für HTTP-Requests zu Dritten aufzeichnet und was für Cookies einem so angeboten werden. Wenn ihr auch mal nachschauen wollt vergesst aber nicht alle Plugins wie Adblocker und so auszuschalten, um das Ergebnis nicht zu verfälschen.
Der erste Baustein in meiner Vorsorge ist eine Vorsorgevollmacht. Damit könnt ihr einer Person eures Vertrauens weitgehende Vollmachten geben um eure Angelegenheiten zu regeln, wenn ihr dazu nicht in der Lage seit. Das ist auch in einer Ehe wichtig, es regelt sich nämlich auch dort nicht alles von alleine.
Das Bundesministerium der Justiz (BMJ) hat dazu eine Broschüre und ein PDF-Formular. So was gibt es auch wo anders, aber ich habe mich dort bedient.
Nun zu Seite. Das erste was auffällt ist ein Cookiebanner ‒ das Zeug ist allgemein wirklich die Pest. Aber es gibt zwei gleichwertige Buttons. Das ist ja schon mal was.
Nur teilt mir der Text gar nicht mit, in was ich hier eigentlich einwilligen soll. In die Nutzung von Cookies jedenfalls nicht, denn zu dem Zeitpunkt sind schon drei Cookies auf meiner Platte. Was da eigentlich gemeint ist wird klar, wenn man es mal ausprobiert: es wird dann nämlich ein Cookie gesetzt, das für das Tracking des Besuchers durch die Software Matomo genutzt wird. Gleichzeitig wird auch ein JavaScript von einer Adresse piwik.itzbund.de geladen. Piwik ist dabei der alte Name von Matomo. Bei der Informierten Einwilligung ist also noch Luft nach oben (und nein, die Datenschutzerklärung habe ich nicht gelesen).
Das Informationstechnikzentrum Bund ist übrigens so was wie der ausgelagerte IT-Dienstleister der Bundesverwaltung und eine Bundesbehörde.
Ansonsten wirkt die Webseite des BMJ aber harmlos. Ich will sogar noch positiv hervorheben, dass die Texte sogar unter eine Creative Commons gestellt sind, hätte ich ja nicht erwartet.
Nächste Anlaufstelle ist das Bundesministerium für Gesundheit. Dort habe ich mich zu einer Patientenverfügung informiert. Mit so einem Dokument könnt ihr vorab festlegen wie ihr medizinisch behandelt werden wollt, für den Fall, dass ihr euren Willen später nicht mehr äußern könnt.
Auch hier spring einen gleich ein Cookiebanner ins Gesicht. Dieses wird von der dänischen Firma Usercentrics und deren Produkt Cookiebot beigesteuert und ist ein Beispiel für Dark Patterns. Zwar ist nur das (angeblich) notwendigste vorausgewählt, aber der Button „Trotzdem alles akzeptieren“ ist farblich hervorgehoben und dort, wo üblicherweise der OK-Button positioniert wäre. Und warum braucht man überhaupt sieben Cookies für eine Informationsseite? Die Wahl eines Dienstleisters für die Verwaltung der Einwilligung ist vielleicht rechtlich in Ordnung, aber ich finde es persönlich verwerflich und hat nichts mit Datensparsamkeit zu tun..
Vom Gesundheitsministerium gelangt man zur Verbraucherzentrale die ein Online-Tool zum Erstellen einer Patientenverfügung anbieten.
Dort wartet diesmal kein Cookiebanner auf den Besucher. Auch Cookies selbst kann ich nicht feststellen. Dafür verwendet die Verbraucherzentrale einen externen Dienst um das Tool anzubieten. Es handelt sich um einen Service der Firma Bryter. Das ist allem Anschein nach eine Low-Code-Plattform, also ein Tool um zu Programmieren ohne klassische Programmiersprachen lernen zu müssen. Das impliziert eine Reihe von HTTP-Request von Servern dieser Firma und zusätzlich einem Zugriff auf eine Domain in der Amazon Cloud.
Die Verbraucherzentrale weißt auch in den Datenschutzhinweisen auf die Nutzung von Bryter hin. Allerdings scheint diese nicht ganz aktuell zu sein. Bryter hat seine deutsche Niederlassung mittlerweile in Frankfurt nicht mehr in Berlin. Wobei, so ganz kann ich nicht sagen wo die Firma sitzt. Im Impressum auf bryter.com finde ich den Firmennamen „Bryter US inc.“. In der Datenschutzerklärung von Bryter aber eine „Bryter GmbH“. Ich persönlich finde ja, dass so etwa höchstpersönliches wie eine Patientenverfügung nicht über Dritte abgewickelt werden sollte, schon gar nicht über US-Server wie die von Amazon.
Die Bundesnotarkammer erlaubt es die Unterlagen, die zuvor erstellt wurden, in einer zentralen Datenbank zu registrieren. Das hat den Zweck, dass Gerichte und Ärzte ermitteln können, ob eine Vollmacht oder Patientenverfügung vorliegt. Dabei werden nicht die Dokumente, sondern nur die Art der Dokumente und die Kontaktdaten erfasst.
Die Reservierung kann auch Online im Zentralen Vorsorgeregister erfolgen. Das Onlinetool setzt ein paar Session-Cookies, aber dankenswerterweise kein Cookiebanner und ich kann auch keine HTTP-Requests an Dritte feststellen. Als ich angefangen habe diesen Artikel zu schreiben wurden noch Schriftarten von Typekit geladen. Das wurde aber auf meinen Hinweis hin abgestellt. Ein vorbildliches Verhalten, wenn man Vertrauen wiederherstellen möchte.
]]>Unter Debian ist dieses Vorhaben einfach umzusetzen. Es gibt bereits ein fertiges Paket das man nur Installieren muss, ein paar Konfigurationswerte anpassen und schon kann es losgehen. Aber der Reihe nach…
Zuerst die Installation mit…
sudo apt-get -y install dropbear-initramfs
Danach Dropbear konfigurieren um den Port festzulegen und den Befehl zum Entschlüsseln direkt nach dem Verbindungsaufbau auszuführen. Dazu in der Datei /etc/dropbear-initramfs/config
die folgende Zeile einfügen:
DROPBEAR_OPTIONS="-c cryptroot-unlock -p 2222"
Anschließend ist es notwendig für Dropbear eine IP-Adresse festzulegen um den Server auch zu diesem frühen Zeitpunkt im Bootprozess ansprechen zu können. Dies geschieht in einer anderen Datei /etc/initramfs-tools/initramfs.conf
:
IP=192.168.1.20::192.168.1.1:255.255.255.0:server.example:eno1
Dabei nicht vergessen die IP, das Gateway und die Netzmaske an die eigenen Gegebenheiten anzupassen.
Die eigentliche Konfiguration ist damit abgeschlossen. Was allerdings noch fehlt sind die öffentlichen Schlüssel der Personen die sich anmelden sollen. Diese müssen jeweils als eine Zeile in die Datei /etc/dropbear-initramfs/authorized_keys
eingefügt werden. Die Schlüssel können unter Windows mit dem Tool PuTTYgen erzeugt werden. Das Tool erhält man zusammen mit dem SSH-Client PuTTY.
Damit alles wirksam wird muss man noch das Initial RAM Filesystem aktualisieren:
sudo update-initramfs -u
Anschließend kann man den Server neustarten und versuchen eine eine SSH-Verbindung herzustellen. Dabei nicht vergessen den Port 2222 anzugeben. Der Benutzer ist immer root und zur Authentifizierung kommt einer der privaten Schlüsseln zum Einsatz deren Konterparts vorher in die Datei authorized_keys
eingetragen wurden.
Vor der Installation ist ein Installationsmedium zu präparieren. Heutzutage ist das bevorzugte Medium ein USB-Stick. Mittels Rufus lässt sich dieser als bootfähiges Installationsmedium herrichte. Zusätzlich braucht man noch ein Installations-Image für Debian. Bei den heutigen Bandbreiten, selbst in Deutschland, ist es nicht nötig eines der umfangreichen Images runterzuladen. Das recht kompakte Net-Install-Image reicht völlig.
wenn der Server vom USB-Stick gebotet wurde gibt es eine erste Auswahl zu treffen. Es gibt die Auswahl zwischen dem grafischen Installer und einem der klassisch auf der Kommandozeile. Dazu kommen noch „Advanced Options“ und zwei Optionen für Menschen die nicht oder nicht gut sehen können. Ich wähle den grafischen Installer, der ist recht komfortabel und leitete durch die nachfolgenden Schritte zu einem funktionierenden System.
In den ersten Schritte legt man Sprache, Land und Tastaturlayout fest. Anschließend bekommt der Server einen Namen und eine Domain. Im Fall von einem lokalen Netzwerk kann man da einfach local nehmen oder sonst irgendwas das nicht zu Konflikten mit „echten“ Webadressen führt.
Als nächstes werden die Benutzer eingerichtet. Mit dem Superuser root fängt es an. Dieser kann mit einem Passwort versehen werden. Ich verzichte aber darauf was dazu führt das dieser für die Anmeldung gesperrt wird. Damit man trotzdem noch Dinge anstellen kann muss deshalb gleich noch ein persönlicher Account angelegt werden. Dieser erhält gleich alle Recht um sich mit dem Kommando sudo
als Root auszugeben.
Der Schwierigste Teil, finde ich, ist die Partitionierung. Ich habe auf einer Disk bzw. SSD einen verschlüsselten Container angelegt der alles aufnimmt außer den nachfolgend genannten Verzeichnissen. Dafür gibt es auch einen kleinen Assistenten der alles schon so hinbastelt. Für die Verzeichnisse /var
und /opt
habe ich dann die restlichen Festplatten in meinem Server mit LVM zu einer großen virtuellen Platte zusammengefast und auch darauf einen Crypto-Container angelegt. In diesem sind dann zwei Partitionen für die genannten Verzeichnisse enthalten. /var
nimmt dabei alle veränderlichen Daten, also das wichtige Zeug, auf und in /opt
wird die Software installiert die ich in meinem Heimnetz haben möchte.
Nach diesem Punkt gibt es noch ein paar Dinge zu erledigen die aber schnell von der Hand gehen. Für den Paketmanager mit den vielen tollen Dingen die Debian so zu bieten hat wird ein Spiegelserver möglichst in der Nähe des eigenen Standorts festgelegt. Zusätzlich kann ein Proxy, falls vorhanden, festgelegt werden. Auch kann man nach eigenem ermessen zustimmen das statistische Daten über die Nutzung erhoben werden dürfen.
Anschließend kann man noch ein paar zusätzliche Module installieren von denen das Debian-Team wohl ausgeht das sie häufig benötigt werden. Ich beschränke mich dabei auf SSH um gleich nach der Installation vom Sofa aus weiterarbeiten zu können.
Zum Abschluss wird noch der Bootloader installiert und ein Neustart durchgeführt. Fertig!
20.02.2022: Diesen Artikel habe ich komplett neu geschrieben. Wenn jemand den ursprünglichen Text noch für historische Forschung 😊 braucht habe ich davon noch einen PDF-Export gemacht.
]]>Als erstes die langweiligen Basics: Ein Webfont besteht aus mehreren sogenannten Schnitten die jeweils in einer Datei vorliegt. Ein Schnitt kann z.B. eine fette oder kursive (oder beides gleichzeitig) Variante sein. Weil aber ein Standard nicht genug ist gibt es für diese Dateien verschiedene Formate die in verschiedenen Browsern genutzt werden können.
Mit diesem Wissen können wir jetzt die Font-Face-Declaration in CSS nutzen um die einzelnen Schnitte/Dateien einem Namen und den gewünschten Parametern zuzuordnen. Im folgenden am Beispiel einer normalen Fette in Kursiv und „normal“
@font-face {
font-family:'Inria Serif';
src: url('InriaSerif-Italic.eot');
src: url('InriaSerif-Italic.eot?#iefix') format('embedded-opentype'),
url('InriaSerif-Italic.woff2') format('woff2'),
url('InriaSerif-Italic.woff') format('woff'),
url('InriaSerif-Italic.svg') format('svg');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family:'Inria Serif';
src: url('InriaSerif-Regular.eot');
src: url('InriaSerif-Regular.eot?#iefix') format('embedded-opentype'),
url('InriaSerif-Regular.woff2') format('woff2'),
url('InriaSerif-Regular.woff') format('woff'),
url('InriaSerif-Regular.svg') format('svg');
font-weight: 400;
font-style: normal;
}
Mit font-family
kann man jetzt ganz normal auf die Schriftart unter dem gewählten Namen zugreifen.
In einem Font kann es alternative Sätze an Glyphen geben. Zum Beispiel gibt es bei der Inria Sans verschnörkeltere Großbuchstaben für besondere Gelegenheiten. Solche Gruppen an Glyphen heißen „Stylistic Sets“. In CSS aktiviert man diese Gruppen mit der Property font-feature-settings
. Die Gruppen sind durchnummeriert. So kann man den Wert für font-feature-settings
aus dem Präfix „ss“ und der zweistelligen Nummer (mit führenden Nullen) zusammensetzen:
.selektor {
font-feature-settings: 'ss01';
}
Welche Stylistic Sets es gibt unterscheidet sich in den verschiedenen Schriftfamilien, da muss man wohl immer etwas recherchieren.
Als Ligaturen bezeichnet man Glyphen die mehrere Buchstaben zusammen darstellen. Das macht man, je nach Sprache bei verschiedenen Buchstaben um ein fließenderes Schriftbild zu erhalten. In der Inria gibt es eine Reihe von Ligaturen für Kombinationen mit dem kleinen f. Um diese zu nutzen muss man gar nichts machen, bei Inria sind die Ligaturen standardmäßig aktiv. Wenn man doch Einfluss nehmen will gibt es dafür die Eigenschaft font-variant-ligatures
.
Für Nummern kann ein Schriftdesigner, ähnlich wie die Stylistic Sets, verschiedene Glyphen anbieten. Inria unterstützt Ziffern im alten Stil. Dabei werden die verschiedenen Ziffern mit etwas Unter- oder Oberlängen angezeigt.
Eine andere Darstellungsform ist für Zahlen und Beträge gedacht die untereinander angeordnet werden. Dabei haben alle Ziffern die gleiche Breite.
Beide Darstellungen kann man in CSS mit der Eigenschaft font-variant-numeric
steuern. Die relevanten Werte sind oldstyle-nums
beziehungsweise tabular-nums
.
Etwas das auch über font-variant-numeric
gesteuert wird ist die Darstellung von Brüchen. Anstatt den Zähler und Nenner mit einem Schrägstrich getrennt nebeneinander anzuzeigen kann man, ähnlich wie bei Ligaturen, ein solches Muster in eine schönere Darstellung umwandeln. Der passende Wert für die CSS-Eigenschaft ist diagonal-fractions
.
Damit das ganze nicht so trocken ist habe ich dazu Beispiele in meinem Laboratorien hinterlegt.
Andere Fonts können andere Features unterstützen, ich wünsche viel Spaß all die Vielfallt der Typographie zu erkunden.
]]>/opt/seafile/
, die Daten hingegen in /var/seafile/
.
Wer noch eine ältere Version von Seafile nutzt muss erst mal auf 7.0.5 updaten da der Updateprozess zu 7.1 sonst nicht funktioniert.
Um ein Update von Seafile vorzubereiten ist der erste Anlaufpunkt bei mir immer die Upgrade Notes für das gewünschte Release. Dort erfährt man auch sofort das jetzt von Python 2 zu Python 3 gewechselt werden muss. Leider nicht so offensichtlich ist die Tatsache das die Version die Debian 9 davon ausliefert nicht ausreicht um Seafile zu betreiben. Ich habe daher auf die harte Tour gelernt das zu den Mindestvoraussetzungen Debian 10 gehört.
Nach einer Migration des Servers auf Debian 10, die reibungslos funktioniert hat, kann man dann die benötigte Python 3 Installation durchführen und auch gleich die zusätzlichen Bibliotheken runterladen. Die Befehle dafür sind in den Upgrade Notes angegeben:
sudo apt-get install python3 python3-setuptools python3-pip -y
sudo pip3 install --timeout=3600 Pillow pylibmc captcha jinja2 sqlalchemy \
django-pylibmc django-simple-captcha python3-ldap
Die Reihenfolge, erst Debian updaten dann Python 3 installieren, ist dabei wichtig. Anderenfalls sind nach dem Update veraltetet Stände der Bibliotheken aus PIP auf dem System und es funktioniert trotzdem nicht. In diesem Fall kann man den zweiten Befehl aber mit dem Parameter --upgrade
erneut ausführen um alles zu aktualisieren. Wichtig dabei: Wer das nicht berücksichtigt erhält keine Fehlermeldung im Log. Seahub wird einfach ohne Angabe von Gründen nicht gestartet.
Das Update selbst lief dann wie gewohnt ab:
Mein Setup ist echt einfach, lest also wirklich die Upgrade Notes für die Details.
Nachdem das soweit alles erfolgt ist bin ich gleich auf das nächstes Problem gestoßen. Aus irgendeinem Grund berücksichtig Seafile 7.1 nicht mehr das die Datenbank und die Dateien nicht innerhalb des Installationsverzeichnisses liegen. Der Updateprozess legt zwar einen Sym-Link auf das richtige Verzeichnis unter /opt/seafile/seafile-data/
an. Genutzt wird der aber offensichtlich nicht. Als Folge wird eine leere Datenbank unter /opt/seafile/seafile-data/
angelegt, was natürlich nicht funktioniert.
Ich weiß nicht warum, wieso und weshalb aber ich habe mir beholfen indem ich alle Dateien vom bisherigen Speicherort nach /opt/seafile/seafile-data/
kopiert habe. Das bedingt aber eine Anpassung am Backup-System. Ich werde in ein paar Monaten den ganzen Server mit Debian 11 neu aufsetzen und investiere bis dahin keine weitere Energie in die Recherche zu diesem Problem.
Nachdem die genannten Probleme umschifft sind startet Seafile wieder und kann bislang auch problemlos genutzt werden. Ich bin mal gespannt ob noch irgendwelche Probleme auftauchen die sich bisher versteckt haben.
]]>Eine etwas schmalere Lösung die wirklich nur die Zeit abruft ist mit systemd-timesyncd verfügbar. Für Raspberry Pi OS, einen Ableger von Debian für die namensgebende Platine, ist das auch der vorgesehene Weg.
Die Installation entfällt auf dem genannten Betriebssystem weil es bereits zum grundlegenden System dazu gehört. Lediglich die Konfiguration kann man noch anpassen.
Wenn man prüfen will wie der Status der Zeitsynchronisation auf dem eigenen Server ist kann man den Befehl timedatectl status
benutzen. Dieser gibt Auskunft über die Zeit und ob eine Synchronisierung mittels des Protokolls NTP vorliegt.
Es gibt drei priorisierte Arten über die der NTP-Server ermittelt werden kann. Auf Ebene der einzelnen Netzwerke (zum Beispiel des heimischen WLans) kann ein Server gesetzt sein. Wenn dies nicht der Fall ist wird eine Liste von NTP-Server herangezogen und danach eine zweite Liste von NTP-Servern (Fallback).
Der NTP-Server des Netzwerks wird nicht durch die Konfiguration von systemd-timesyncd eingestellt sondern ist Teil der manuellen oder automatischen (DHCP) Netzwerkeinstellungen.
Die beiden Listen können aber in der Konfigurationsdatei /etc/systemd/timesyncd.conf festgelegt werden. Dort kann in einer Zeile beginnend mit NTP= eine Liste von NTP-Server angegeben werden. Die Einzelnen Einträge werden mit Leerzeichen getrennt. Die zweite Liste beginnt mit FallbackNTP =. Die Einträge hier werden genutzt wenn die Server aus der ersten Liste alle nicht verfügbar sind.
Nach Änderungen kann man diese wirksam werden lassen in dem man timesyncd neu startet:
sudo systemctl restart systemd-timesyncd
Mittels timedatectl show-timesync --all
kann man sich ausgeben lassen welche NTP-Server in allen drei Prioritätsstufen konfiguriert sind und welcher aktuelle genutzt wird.
Die einfache Nutzung von timedatectl status
habe ich ja oben schon angesprochen. Mit timedatectl timesync-status
bekommt zusätzliche Informationen aus dem technischen Unterbau des NTP.
Wenn man eine Fritzbox als Router benutzt kann diese auch als NTP-Server für alle Computer im Netzwerk fungieren. Im Webinterface der Fritzbox links auf Heimnetz und dann auf Netzwerk klicken. Im rechten Bereich anschließend den Tab Netzwerkeinstellungen öffnen und dort im Abschnitt Zeitsynchronisation die gewünschten, externen Server angeben und einen Hacken setzen. Den Namen fritz.box von allen Fritzboxen muss dann wie oben beschrieben in der Konfiguration angeben.
Als externen Zeitserver für die Fritzbox würde ich das NTP Pool Project empfehlen.
]]>Mit BackstopJS kann man Screenshots einer Webseite machen um diese mit einer Referenz zu vergleichen. Sollten einzelne Pixel voneinander Abweichen bemerkt das Tool dies und schlägt Alarm.
Die Installation ist über NPM einfach durchgeführt.
npm install --save-dev backstopjs
Ich gehe dabei davon aus das es sich um ein Projekt handelt dessen Dependency Management mit NPM gemacht wird. Anderenfalls müsste man BackstopJS global installieren. Anschließend kann man das Tool für sein eigenes Projekt initialisieren. Dabei wird eine Konfigurationsdatei backstop.json erzeugt.
npx backstop init
Grundsätzlich wäre Backstop jetzt schon einsetzbar. Allerdings simuliert das Tool einen Browser. Damit wird auch ein Server benötigt von dem BackstopJS die Anwendung abrufen kann. Je nach dem um was es geht gibt es verschiedene Applikationsserver die man nutzen kann. Ich brauche für meinen Bedarf nur einen einfachen Webserver. Ich habe mich dabei ohne besonderen Grund für http-server entschieden.
npm install --save-dev http-server
Um später sowohl den Server als auch BackstopJS automatisiert nutzen zu können setze ich auch noch Concurrently ein. Dazu weiter unten mehr.
npm install --save-dev concurrently
Die zuvor erstellte Konfigurationsdatei enthält drei Bereiche: Viewports, Szenarien und Einstellungen.
Mit Viewports kann man die Maße des Browserfensters festlegen wenn die Screenshots durch BackstopJS angefertigt werden. Dabei sind mehrere Größenangaben möglich. Die Testfälle werden dann für jede dieser Größen durchgeführt. So kann man Tests für verschiedene Displaygrößen, z.B. von Smartphones und Desktops, durchführen.
Mit Szenarien beschreibt man die einzelnen Testfälle. Jedes Szenario wird primär durch einen Namen (label) und zwei URLs beschrieben. Die Referenz-URL (referenceUrl) wird aufgerufen wenn BackstopJS die Screenshots mit dem Soll-Zustand erzeugt. Damit werden dann Screenshots verglichen die von der Seite hinter der Test-URL (url) angefertigt werden.
Durch die zwei verschiedenen URLs lässt sich das Referenzmaterial z.B. von einem produktiven System anfertigen. Die Test-Screenshots möchte ich natürlich vom Entwicklungsstand und damit von http-server beziehen.
Wenn auf der Testseite Dinge sind die nicht relevant für den Test sind kann ich mittels der Angabe selectors eine Liste von CSS-Selektoren angeben. Anschließend beschränkt sich der jeweilige Screenshot auf die Elemente die diesen Selektoren entsprechen.
Wenn es darum geht dynamische Inhalte zu testen muss ich irgendwie die Interaktion des Nutzers simulieren. Um das zu ermöglichen setze Backstop auf Puppeteer. Puppeteer zu erklären würde den Artikel sprengen. Nur soviel: Mit onReadyScript lässt sich ein Script angeben das ausgeführt wird sobald die Testseite geladen wurde.
Alles was nicht Viewport oder Szenario ist fasse ich mal unter Einstellungen zusammen. Hauptsächlich geht es um Speicherorte für die Screenshots und die Testergebnisses und -berichte. Dazu kommen Parameter für den Browser und Puppeteer.
Eine Wert der später noch wichtig wird ist die Angabe der Art der Ausgabe des Testergebnisses. Mit report lässt sich festlegen ob ein menschen lesbarer HTML-Report eine JUnit XML- oder eine JSON-Datei (oder alles gleichzeitig) erzeugt werden soll.
Für die folgenden Schritte braucht man eine aufrufbare Fassung der zu testenden Webseite. Genau dafür habe ich vorher http-server installiert. In einer Shell kann ich den Server mit npx http-server -p 3001 -c-1
starten. In einer zweiten Shell kann ich dann die unten stehenden Befehle ausführen.
Das Arbeiten mit BackstopJS vollzieht sich in drei Phasen. Alles beginnt mit dem anfertigen der Referenzbilder: Von einer validen Fassung der zu testenden Webseite oder -app.
npx backstop reference
Nach der Entwicklung einer neuen Version folgt dann der Test. Erneut werden Screenshots erzeugt, mit den Referenzbildern verglichen und ein Report erzeugt.
npx backstop test
Anhand des Reports geht es dann an die Bewertung der Abweichungen. Solange etwas nicht zufriedenstellend ist wiederholt sich der Zyklus aus Entwicklung und Test. Sobald ich nichts mehr zu bemängeln habe wird die dritte Phase abgeschlossen indem die Referenzen aktualisiert werden.
npx backstop approve
Wie ich eben ausgeführt habe werden des öfteren Screenshots angefertigt. Da diese als Dateien gespeichert werden muss ich im Zusammenspiel mit Git ein paar Besonderheiten berücksichtigen. Git ist nicht besonderst gut darin Binärdaten zu managen. Damit das nicht zum Problem wird gibt es Git LFS. Damit werden Binärdateien im Git-Repository durch Pointer ersetzt während die Dateien außerhalb abgelegt werden.
Zuerst muss ich es für meine Git-Benutzereinstellungen herrichten, etwas das auf jedem Endgerät wiederholt werden muss:
git lfs install
Anschließend muss ich dem Git-Repository noch mitteilen für welche Dateien LFS zuständig sein soll. Im vorliegenden Fall wären das alle Bilddateien aus dem Ordner mit den Referenzbildern.
git lfs track "backstop_data/bitmaps_reference/*"
Der Befehl führt zu einer Eintragung in der Datei .gitattributes. Diese habe ich sinnvollerweise auch eingecheckt. Im Gegensatz dazu brauche ich weder die Screenshots aus den Tests noch die Reports in meinem Repository. Deshalb gehören die drei entsprechenden Ordner auf die Ignore-List:
backstop_data/bitmaps_test/
backstop_data/html_report/
backstop_data/ci_report/
Testen wird schnell lästig darum automatisiere ich alles wenn möglich. In meinem Fall besteht die Lösung aus einer Kombination aus NPM-Scripting und GitHub Actions.
Das folgende Script, in die Datei package.json eingefügt startet den Test:
"test:visual": "concurrently --success=first --kill-others \"npx http-server -p 3001 -c-1\" \"npx backstop test\"",
Der erste Teil (concurrently) führt mehrere Befehle parallel aus. Dabei werden alle parallelen Prozesse abgebrochen (--kill-others
) sobald einer davon beendet ist (--success=first
).
Im ersten der parallelen Befehle (http-server) wird der Webserver gestartet. Der zweite Befehl führt den eigentlichen Test aus.
Dieses Script kann jetzt in einer Action ausgeführt werden. Dabei lauern aber zwei Fallstricke:
Ich muss beim Checkout innerhalb der Action LFS aktivieren.
- uses: actions/checkout@v1
with:
lfs: true
Außerdem sind die Referenzbilder vom Entwicklungsrechner nicht unbedingt passend für die Laufzeitumgebung innerhalb der Action. Ich entwickle auf Windows und die Actions laufen üblicherweise mit Ubuntu. Das führt zu leichten Unterschieden im Rendering und damit auch in den Screenshots. Ich umgehe das Problem einfach indem ich auch die Referenzbilder immer neu erstelle, natürlich nicht von der neu entwickelten Version des Projektes sondern von der produktiven.
Das Projekt das ich zum ausprobieren verwendet habe kann auf GitHub eingesehen werden. Um es etwas komplizierter zu machen verwende ich dabei abweichend von oben aber eine Javascript-Konfiguration .backstoprc.js anstelle der JSON-Datei.
]]>Mit dem neuen Media-Feature screen-spanning lässt sich auf Geräten mit zweigeteiltem Display ein spezielles Styling anwenden. Momentan kann man damit zwischen vertikal oder horizontal geteilten Displays unterscheiden. Das Feature ist mir für meine Testseite zu Media Queries endlich mal was neues und wurde daher auch gleich eingebaut. Beim ausprobieren aber nicht vergessen die experimentellen Features zu aktivieren (edge://flags/#enable-experimental-web-platform-features)
Das erkennen von betroffenen Geräten ist ein erster Schritt. Um die Position und besonderst die Breite des Bereichs zwischen den Displayteilen in seinem Stylesheet berücksichtigen zu können gibt es zusätzlich neue Variablen mit denen es möglich ist die Aufteilung der eigenen Inhalte auf die Teile des Displays zu organisieren. Ich hab das für die oben erwähnte Testseite auf Basis eines Gridlayouts beispielhaft gemacht. Aber auch hier sind die Möglichkeiten auf den einfachen Fall eines zweiteiligen Displays beschränkt.
@media all and (screen-spanning: single-fold-vertical) {
body {
display: grid;
grid-template-areas:
"header nav"
"main nav"
"main aside"
"main footer";
grid-template-columns: env(fold-left) calc(100% - env(fold-left) - env(fold-width));
grid-template-rows: auto auto 1fr auto;
grid-gap: 0px env(fold-width);
}
}
Parallel zu CSS gibt einem Microsoft auch die Möglichkeit mittels Javascript auf die Teilung von Displays zuzugreifen. Hierbei wurde mit der Window Segments Enumeration API aber ein flexiblerer Ansatz gewählt der gut für verschiedenste Anordnungen von beliebig vielen Displays klar kommen sollte. Eine Einschränkung besteht konzeptionell lediglich darin das die Displays rechteckig sein müssen. Das liegt daran das die Beschreibung eines Teils der Anzeigefläche über einen Punkt (links oben) und einer Breite und Höhe geschieht. Diese Informationen kann man den Einträgen eines Arrays entnehmen das man wiederum mit window.getWindowSegments()
erhält.
Die Möglichkeiten im Styling mittels CSS sind noch beschränkt auf den einfachsten Fall, trotzdem ein pragmatischer Ansatz für die aufkommende Geräteklasse mit geteilten oder faltbaren Displays. Im Bereich Javascript gibt es diese Einschränkung nicht.
Ich hätte mir aber gewünscht das die Implementierung nicht nur mobile Geräte wie das Surface Duo unterstützen würde. Auch bei Desktops gibt es durchaus Multimonitorkonfigurationen die sich in der Window Segments Enumeration API niederschlagen könnten sobald sich ein Browserfenster über mehrere Monitore erstreckt.
]]>Wie der Name schon sagt, geht es darum, die Rollbalken des Browsers an den Style der eigenen Seite anzupassen. Dazu werden zwei neue CSS-Eigenschaften erfunden. Eine definiert Farben, die Andere bestimmt die Breite.
Ein Wort vorweg: wie bei allen Eingriffen in die UI(User Interface) des Browsers muss man sich bewusst sein, was man tut. Jede Änderung bricht mit der Erwartung des Benutzers und kann sich auf die Zugänglichkeit auswirken. Bezüglich letzterem ist besonders zu berücksichtigen, den Kontrast in den Farben hoch genug zu wählen.
Es handelt sich noch um einen sehr frühen Stand, daher werden beide Eigenschaften bislang nur von einem Browser unterstützt: Firefox.
Mit der Eigenschaft scrollbar-color
lässt sich sowohl die Farbe des „Schiebers“ als auch die Hintergrundfarbe einstellen. Dazu verwendet man einfach zwei Farbangaben mit einem Leerzeichen getrennt:
Die Scrollbar des Fensters hängt dabei am HTML-Element. Für innere Elemente wie z.B. Textarea-Elemente funktioniert die Eigenschaft natürlich auch.
Leider gibt es keine einzelnen Eigenschaften für die beiden Farben. Also muss man immer beides setzen. Das ist deswegen problematisch, weil man so nicht eines der beiden (z.B. den Hintergrund) auf dem Standard des Systems belassen kann.
Wenn man die Farben anpassen will, während gescrollt wird, kann man dies über Pseudoklassen (hover, focus, active) erreichen. Allerdings ist dabei zu beachten, dass Firefox immer noch eine Abdunklung oder Aufhellung der Farbe des Schiebers vornimmt.
Man kann anstelle der beiden Farbwerte auch eines der beiden Themes light
und dark
angeben. Ich denke mal, das ist für Systeme, die eine Art Dark-Mode der UI unterstützen. Bei mir hatte aber keiner der Werte eine Auswirkung.
Mittels scrollbar-width
lässt sich einer von drei Werten auswählen, der die Breite der Scrollbar bestimmt. Es besteht die Auswahl zwischen auto
für das „normale“ Design des Browsers, thin
für eine schmale Darstellung und none
zum Ausblenden der Scrollbars.
Mit none
kann der Bereich trotzdem noch gescrollt werden, zum Beispiel über das Scrollrad einer Maus. Das Ganze macht vielleicht in Kombination mit den Pseudoklasse Hover und Fokus Sinn. Damit kann man den Scrollbalken ausblenden, bis er gebraucht wird. Allerdings ändert sich dadurch auch die Breite der Box, was zu einem anderen Textumbruch führen kann.
Den Wert thin
hingegen, kann ich mir ganz gut vorstellen, bei Scrollbars innerhalb einer Seite. Unter Windows 10 wirken diese so nicht ganz so brachial als Fremdkörper.
Die Eigenschaft wird übrigens nicht vererbt. Man kann sie also nicht an einem Element setzen und erwarten, dass sie sich auch bei allen Kindern auswirkt. Stattdessen muss man sie überall deklarieren, wo man sie auch wirklich braucht (oder eben mit dem *-Selektor überall).
Nicht gerade das wichtigste Feature auf der Welt, aber ganz nett. Insbesondere die schmaleren Scrollbalken können bei Textareas recht schick sein. Ich hoffe aber, dass diese Einflussnahme in die UI durch die Nutzer leicht deaktivierbar sein wird. Ein paar Gruppen könnten sonst Probleme haben die schmalen Scrollbars zu bedienen oder die gefärbten überhaupt richtig wahrzunehmen.
Da ein Mitarbeiter von Microsoft an der Spezifikation arbeitet, besteht die Hoffnung, dass Chromium im Rahmen des neuen Edge Browsers auch bald unterstützt wird?
]]>Um das Innere einer Web Component zu kapseln verwendet man sogenanntes „Shadow DOM“. So erreicht man das nach außen nur ein einzelnes HTML Element sichtbar ist. Dadurch bedingt kann man aber auch keinen CSS-Selektor mehr erstellen der dieses Shadow DOM adressiert. Daher sehen Web Components immer so aus wie der Entwickler der Komponente es vorgesehen hat. Ein Einfluss von außen ist erst mal nicht vorgesehen. Um ein einheitliches Look and Feel in einer Webanwendung zu erhalten ist aber genau das wünschenswert.
Im Verlauf der Zeit gab es verschiedene Ansätze um eine solche Möglichkeit zu schaffen. Ich beschreibe im folgenden die Ansätze die momentan (also im November 2019) anwendbar sind und halbwegs praktikabel erscheinen. Vorweg: Eine einzelne übergreifend und flexibel nutzbare Lösung habe ich nicht.
13.09.2020: Die in diesem Abschnitt beschriebene Herangehensweise ist hinfällig geworden. Firefox unterstützt mittlerweile das Pseudoelement ::part() und kann daher die darauf aufbauende Lösung weiter unten nutzen. Zudem hat Chromium generell Untersützung für das das Schlüsselwort revert realisiert, leider aber nicht als Fallback für Custom Properties. So wie Chromium arbeitet macht der Ansatz hier sogar den Style in solchen Browsern kaputt.
In CSS gibt es mittlerweile eine Art Variablen um konkrete Werte mehrfach in seinem Stylesheet für Eigenschaften setzen zu können. Solche Variablen werden als Custom Properties bezeichnet.
Für die vorliegende Aufgabenstellung sind sie interessant weil sie die Grenzen des Shadow DOMs überwinden. Das bedeutet, außerhalb einer Web Component deklarierte Custom Properties können innerhalb der Web Component genutzt werden um das Shadow DOM zu stylen.
Wenn der Entwickler der Komponente Custom Properties als Werte nutzt und diese dann nicht deklariert wird kommt der angegebene Fallback zum tragen. Wenn ein solcher nicht angegeben wurde wird „unset“ angenommen. Das bedeutet die verschiedenen originalen Styles der Browser werden ausgehebelt.
Am Beispiel eines Buttons wird das Problem deutlich. Dieses Element sieht auf den verschiedenen Plattformen jeweils so aus wie ein Button nun mal auf der jeweiligen Plattform aussieht. Wenn diesem Button nun eine oder mehrere Custom Properties zugewiesen werden legt der Entwickler damit das Aussehen auf genau einen Wert fest: den Wert der Custom Property oder eben unset.
Solange Komponente und die nutzende Seite aus einer Hand kommen kann man noch sicherstellen das alle Custom Properties auch deklariert werden. Sobald aber mehrere Parteien ins Spiel kommen ist das aber schon sehr schwierig.
Eine Möglichkeit dieses Problem zu umgehen ist das Keyword „revert“ bei der Zuweisung als Fallback zu setzen. Revert sorgt dafür das die Eigenschaft auf den vom Browser vorgegebenen Wert gesetzt wird. Somit wird also nicht mehr unset angenommen. Das Problem dabei ist allerdings das momentan nur Firefox und Safari das Schlüsselwort unterstützen.
Schon für ein einzelnes HTML-Element gibt es eine Vielzahl an CSS-Eigenschaften. Für jede davon eine Custom Property vorzusehen ist enorm aufwändig. Bei komplexeren Konstrukten im Shadow DOM wird es schlicht unpraktikabel.
Noch schlimmer wird es wenn Pseudoklassen wie focus oder hover ins Spiel kommen. Dann müssen auch für diese wiederum Custom Properties vorgesehen werden.
Zusammen betrachtet kann diese Technik nur sehr selektiv eingesetzt werden. Etwas besseres für Firefox ist mir aber noch nicht untergekommen um ein wenig externe Farbe in Web Components zu bringen.
Goggle hat einen aktuellen Lösungsansatz in Chrome integriert. Das neue Pseudoelement „part“ erlaubt es auf Teile eines Shadow DOMs zuzugreifen. Dazu muss der Entwickler der Komponente das gewünschte Element mittels eines HTML-Attributes das wiederum „part“ heißt mit einem Namen versehen.
<button type="button" part="componentbutton">Ein Button innerhalb eines Shadow DOMs</button>
Anschließend kann der benannte Teil über CSS-Selektoren adressiert werden. So kann auf die übliche Weise das ganze Element neu gestaltet werden. Auch eine Kombination mit Pseudoklassen (fokus, hover) ist im Selektor ebenfalls möglich.
my-component::part(componentbutton) {
background: green;
}
my-component::part(componentbutton):hover {
background: darkgreen;
}
Auf diese Weise wird recht elegant ermöglicht das der Komponentenentwickler einzelne Teile seines Shadow DOMs freigeben kann und gleichzeitig erlaubt es dem Anwendungsentwickler eben diese Teile auf dem herkömmlichen Weg seinem Styleguide anzupassen. Leider unterstützten momentan nur Browser auf Chromium basierend dieses Feature. Zumindest Firefox hat allerdings schon eine experimentelle Implementierung. Hier kann man also hoffen.
Es gab seit der Einführung von Web Components auch ein paar andere Ansätze die aber wieder verworfen wurden. Wer ein bisschen „Altertumsforschung“ betreiben will sei der Artikel „Styling Web Components Using A Shared Style Sheet“ zur Recherche empfohlen.
Das Logo das ich zur Illustration dieses Artikels verwende entstammt einer Sammlung von Icons zum Thema Web Components. Es steht unter einer Creative Commons BY-SA 3.0 und wurde durch WebComponents.org bereitgestellt.
]]>(Geänderte Fassung um Entwicklungen beim Browsersupport von Firefox uns Chromium zu berücksichtigen)
]]>Aufgrund Alterungserscheinungen war es nötig das ich mir einen neuen eBook-Reader anschaffen. Seit 2012 hatte ich einen alt, ehrwürdigen PRS-T1 der Firma Sony der mich viele, viele Stunden begleitet hat. Da Sony keine eBook-Reader mehr produziert musste ich mich anderweitig umsehen. Kindle kommt nicht in frage; das Geschäftsgebaren von Amazon im Bereich der eBooks schmeckt mir nicht. Da gibt es zu viel DRM und zu viel proprietäres Zeug. Die Entscheidung viel dann auf einen Tolino Epos. Der Grund hierfür ist das große Display.
Jedem würde ich das knapp 20cm große Display nicht als Kaufgrund empfehlen. Bei mir ist es aber so das ich neben Romanen auch öfter mal Fachbücher mit Codelistings lese und bei letzterem erhoffe ich mir eine bessere Darstellung durch die größere Fläche. Bei Romanen ist das kompaktere Format des Sony Readers meiner Meinung nach angenehmer zu halten.
Durch die höhere Auflösung wirk der Text auf dem Tolino klarer als auf dem Sony. Auch ist der Hintergrund etwas heller, auch ohne die Hintergrundbeleuchtung zu aktivieren die das alte Gerät von Sony noch gar nicht hatte. Bei aktivierter Beleuchtung wirkt der Hintergrund noch heller, was den Kontrast noch mal etwas verbessert.
ePaper-Displays müssen gelegentlich, kurzfristig schwarz gefärbt werden um Schatten zu beseitigen die von zuvor angezeigten Seiten zurückbleiben. Dieses Verhalten hat mich bei meinem Sony schon nach kurzem nicht mehr gestört. Beim Epos fällt es mir momentan noch negativ auf, das ist aber eventuell der gesteigerten Aufmerksamkeit geschuldet die ich neuen Dingen entgegenbringe.
Ein Unterschied der ins Auge fällt ist die Gehäusefarbe: Warum müssen die Geräte heute alle schwarz sein? Ein weißes Gehäuse, wie das meines PRS-T1, ist irgendwie dichter am gedruckten Buch und fördert die Konzentration auf den Inhalt. Ist aber wahrscheinlich Geschmackssache. Einen Vorteil hat das Gehäuse des Epos gegenüber dem Sony: Das Display ist plan eingelassen.
Wo der Sony-Reader noch richtige Tasten hatte, die allerdings zum Prellen neigten, hat der Tolino Epos nur noch ein Sensorfeld unterhalb des Displays mit dem man zum Hauptmenü zurückkommt. Mich stört es nicht, wegen des Prellens habe ich sowieso meist das Touchdisplay genutzt. Jetzt habe ich einfach nur keine Wahl mehr.
Zur Performance kann ich zum jetzigen Zeitpunkt noch nicht viel sagen. Bei PDFs fühlt sich der Tolino etwas langsam an. Das war aber auch nie eine Stärke des Sony. Der wiederum mit zunehmenden Alter immer langsamer wurde, vor allem beim öffnen von Büchern. Ich weiß allerdings nicht ob das am Füllstand des Speichers, an den heutigen eBooks oder an sonst was liegt.
Beim Tolino gibt es übrigens keinen SD-Karten-Slot und auch keinen Kopfhöreranschluss. Beides habe ich beim Sony bisher auch nie gebraucht. WLAN haben sie hingegen beide.
Das erste was auffällt ist das niedliches StandBy-Bild. Dieses schlafende Tolinogesicht ist aber leider nur 5 Minuten niedlich danach hab ich mich satt gesehen. Was ich wirklich will ist die Anzeige des Buchcovers das ich gerade lese. Ich verknüpfe die Inhalte im Gedächtnis nämlich gerne mit dem Cover. Leider gibt es diese Funktion vor Version 12.2.0 nur auf meinem alten Sony-Reader. Um wenigstens das Tolino-Gesicht los zu werden kann man ein anderes Bild zur anzeige bringen. Dazu muss man lediglich eine Datei mit dem Namen „suspend.jpg“ auf dem Gerät ablegen. Eine Anleitung dazu gibt es auf papierlos-lesen.de. Mit ein wenig Suchen im Internet findet man verschiedene Sammlungen von Bildern in funktionierenden Größen. Ich habe mich aus einer Liste abstrakter Bilder bedient. Seit Version 12.2.0 gibt es in den Einstellungen aber auch die Möglichkeit das Buchcover anzeigen zu lassen. Wenn man aber wie hier beschrieben ein eigenes Bild hinterlegt hat überschreibt dies die Einstellung zum Coverbild im Menü. Die Datei suspend.jpg muss also gelöscht werden damit die neue Option greifen kann.
Wem die Schriftarten nicht gefallen kann die Liste der verfügbaren Fonts einfach ergänzen. Dazu muss man die Font-Datei einfach in einen speziellen Ordner auf dem Gerät kopieren. Details kann man dem Tolino-FAQ entnehmen.
Beim Markieren von Stellen innerhalb von Büchern hat der Sony PRS-T1 trotz seines Alters die Nase vorn. Esselsohren können beide in Seiten machen. Auch können beide Geräte Textstellen markieren, der Epos allerdings nicht in PDFs. Was der Epos aber nicht kann ist Freihandmarkierungen zu malen. Manchmal, zugegebenermaßen Selten, mache ich sowas bei Diagrammen.
Wenn man nicht nur eBooks im EPUP-Format konsumiert sondern auch mal das ein oder andere PDF konnte mit dem Sony-Reader mehr Optionen nutzen. Zum Beispiel kann man mehrspaltigen Seiten in mehrer Kacheln aufteilen um dann von Oben nach Unten und von Rechts nach Links durchzublättern. Auch kann man die Seitenränder wegschneiden um mehr Platz für den Text zu schaffen.
Beide Funktionen bietet der Tolino nicht an. Was er anbietet ist eine Vergrößerung des Textes in mehreren Schritten, die Darstellung im Querformat und eine „nur Text“-Darstellung.
Das Vergrößern ist nur nützlich wenn man einzelne Details, zum Beispiel in einem Diagramm, näher betrachten will. Dabei werden nämlich die Zeilen auf mehrere Bildschirmseiten verteilt so das ein flüssiges lesen nicht mehr möglich ist.
Im Querformat sind PDFs in Oben und Unten geteilt. Damit erhält man eine gute Lesbarkeit. Leider ist die Navigation nur wenig durchdacht. Wenn man auf dem Touchdisplay nach Links wischt kommt man nicht etwa erst von oben nach unten und beim nächsten Wisch zur folgenden Seite sondern gleich eine Seite weiter womit die untere Hälfte einer Seite übersprungen wird. Um dorthin zu kommen muss man von Unten nach Oben wischen. Das führt dazu das man jede halbe Seite einmal umdenken muss.
Die dritte Option extrahiert den reinen Text aus dem PDF und zeigt ihn ohne Formatierung an. Dabei werden auch Fußzeilen mitten im Text positioniert und es entsteht ein fürchterlicher Textbrei.
Positiv kann man aber hervorheben das das große Display ausreichend ist um ganze DIN-A4-Seiten groß genug darstellen zu können das man den Text, gute Augen vorausgesetzt, lesen kann.
Da beide hier verglichenen Geräte WLAN haben, bringen Sie auch beide einen Browser mit. Leider sind diese Browser lieblos und Funktionsarm. Den des Tolinos zu untersuchen habe ich recht schnell aufgegeben. Meine Enttäuschung, und das bezieht sich auf beide Reader, macht sich hauptsächlich an zwei Punkten fest:
Der Browser scrollt durch Webseiten. Das verursacht Schlieren und ein ständiges Flackern. Das ist meines Erachtens dem ePaper geschuldet. Es ist einfach nicht schnell genug fürs Scrolling. Von einem Browser auf einem eReader würde ich erwarten das er Webseiten seitenweise blättern kann. So wie es ist kann ich den Browser nur als Notbehelf
verwenden.
Ein technischer Aspekt der Aufzeigt wie wenig Sorgfalt in den Browser gesteckt wurde betrifft das monochrome Display. CSS Media Queries erlauben es diese Eigenschaft abzufragen. Leider behaupten beide Browser standhaft Sie würden auf Farbdisplays zugreifen können.
Was mir den Spaß auf dem Tolino zusätzlich vermiest ist die fehlende Möglichkeit die Startseite zu ändern. Was soll das? Warum muss ich immer erst einen Ausflug zu Google machen bevor ich Surfen kann?
Tolinos sind mit einer integrierten Shop ausgestattet der dem Online-Shop des Anbieters entspricht bei dem man den Tolino gekauft hat. Das wäre soweit nicht schlimm, leider werden mir lauter nutzlose Buchvorschläge auf dem Launcher präsentiert die ein Drittel der Oberfläche in Anspruch nehmen. Das Feature lässt sich leider auch nicht abschalten.
Mehr kann ich zu diesem Thema nicht sagen da ich meine Bücher lieber am PC kaufe und daher den Epos noch nie zu diesem Zweck genutzt habe.
Noch etwas das das Betriebssystem des Sony dem Tolino voraus hat ist die Verwaltung von Serien und Kategorien. Der Sony kann in kombination mit Calibre die Bücher automatisch in sogenannte Sammlungen einsortieren. Dabei können verschiedene Informationen genutzt werden und die Bücher gleichzeitig in mehreren davon abgelegt werden. Zum Beispiel kann man das Buch „Die Herren von Winterfell“ in Fantasy und in „Game of Thrones“ ablegen. Bei Tolino hingegen muss man etwas tricksen und bekommt doch nur maximal eine Serie, wo heißt das Feature bei Tolino, hin. Andere Serien müsste man von Hand direkt auf dem Gerät zuordnen, was recht nervig ist.
Die Serie wird aus dem Ordner abgeleitet in dem das Buch auf dem Epos abgespeichert wird. Um eine Bestimmte Information aus den Metadaten in Calibre in einen Ordnernamen zu verwandeln muss man in den Einstellungen von Calibre unter „Senden von Büchern auf Geräte“ im Feld „Speichervorlage“ ein neues Muster anlegen nach dem Calibre den Speicherpfad auf dem Gerät bildet. Wenn man diese Muster nicht für alle Geräte ändern will kann man auch, bei angeschlossenem Reader, in der Symbolleiste unter „Gerät“ den Menüpunkt „Dieses Gerät konfigurieren“ wählen und dort wiederum das Feld „Speichervorlage“ ausfüllen. Die Anleitung von Calibre führt auf wie die Vorlagensprache für diese Muster funktioniert.
Ich habe folgendes Muster für mich gewählt. Es erzeugt einen Ordner für das erste Stichwort, einen Unterordner für die Buchreihe und benennt die Datei dann nach Reihe inklusive Nummer, Titel und Author. Wenn keine Stichwort oder keine Reihe festgelegt ist entfallen die jeweiligen Ordner.
{tags:list_item(0,\,)}/{series}/{series:| - |}{series_index:0>2s}_{title}_{authors}
Leider führt es nicht zur Zuordnung von mehreren Serien wenn man die Dateien in tiefen Ordnerstrukturen ablegt. Lediglich die tiefste Ebene führt auch dazu das dass Buch in einer Serie abgelegt wird. In meinem Fall hat also die Reihe Vorrang vor dem ersten Stichwort.
Nach dieser ersten Begutachtung und einer Woche der Nutzung muss ich leider sagen das Sony die Software bereits vor sechs Jahren besser hinbekommen hat. Mit der Hardware bin ich hingegen recht zufrieden soweit man das nach so kurzer Zeit sagen kann.
]]>Die Entwicklung im Bereich der Hardware von eBook-Readern ist klar erkennbar – bei der Software hingegen…
(Geänderte Version unter Berücksichtigung von Version 12.2.0 der Tolino-Software)
]]>Zur API gehört eine Klasse MediaMetadata. Objekte dieser Klasse können an navigator.mediaSession.metadata
übergeben werden und enthalten vier Angaben zum abgespielten Video oder zur Audiodatei: Titel, Artist, Album und ein Array von Bildern.
Ich benutze für meinen Podcatcher die drei dieser Angaben um den Namen der Podcastepisode (Titel), den Namen des Podcasts (Artist) und das Logo des Podcasts anzugeben. Die Angabe zum Album nutze ich nicht. Im Ergebnis erhält man eine Anzeige wie die folgende:
Wie man sieht, wirken sich diese Angaben auf die Darstellung der Notification aus. Das angegebene Bild wird aber auch als Hintergrund des Sperrbildschirms genutzt.
Den folgenden Code muss ich natürlich immer aufrufen wenn eine neue Episode abgespielt wird um die Daten zu aktualisieren.
if ('mediaSession' in navigator) {
const metadata = {}
if (episode.title) {
metadata.title = episode.title
}
if (episode.source) {
metadata.artist = episode.source
}
if (episode.image) {
metadata.artwork = [
{ src: episode.image }
]
}
navigator.mediaSession.metadata = new MediaMetadata(metadata)
}
Warum sich Google zu diesem Zweck für eine Javascript-API entschieden hat verstehe ich nicht ganz. Ich hätte eher zusätzliche Attribute an den HTML-Elementen <audio> und <video> spezifiziert. Das würde dann auch ohne Javascript funktionieren.
Normalerweise werden die Playback-Notifications unter Android nur mit zwei Funktionen versehen. Antippen der Notification bringt den Browser in den Vordergrund der gerade etwas abspielt. Ein Play/Pause-Button tut genau das: Pausieren oder Wiedergabe fortsetzen.
Mit der Media Session API kann man dies um vier Funktionen erweitern. Es wird so möglich Sprünge im aktuelle abgespielte Titel (vorwärts und rückwärts) sowie zum nächsten bzw. vorherigen Titel in einer Playlist zu springen.
Diese zusätzlichen Funktionen können sowohl über entsprechende Symbole in der Notification als auch über Multi-Media-Keys auf Tastaturen oder Headsets ausgelöst werden. Genau dafür hatte ich bisher Listener für Keyboard-Events eingesetzt. Diese haben aber ein Problem: Sie werden nur aufgerufen wenn der Browser mit meinem Podcatcher im Vordergrund ist. Also insbesondere nicht wenn Android den Sperrbildschirm anzeigt – sehr unbefriedigend. Durch die neue Umsetzung auf Basis der API ist das jetzt zu mindestens bei der Nutzung von Chrome besser gelöst.
if ('mediaSession' in navigator) {
navigator.mediaSession.setActionHandler('play', this.play.bind(this))
navigator.mediaSession.setActionHandler('pause', this.pause.bind(this))
navigator.mediaSession.setActionHandler('seekbackward', this.seekBackward.bind(this))
navigator.mediaSession.setActionHandler('seekforward', this.seekForward.bind(this))
navigator.mediaSession.setActionHandler('previoustrack', this.previousTrack.bind(this))
navigator.mediaSession.setActionHandler('nexttrack', this.nextTrack.bind(this))
} else {
document.addEventListener('keydown', (event) => {…}, false)
document.addEventListener('keyup', (event) => {…}, false)
}
Die API ist insbesondere unter Android interessant (iOS habe ich nicht zur Hand um dazu was sagen zu können). Unter Desktop-Betriebssystemen beschränkt sich die Verbesserung auf die Unterstützung der Multimediatasten.
Schön wäre es allerdings wenn sich auch andere Browser darauf einlassen würden und die Metadaten auch ohne den Einsatz von Javascript angegeben werden könnten.
]]>Service Worker stellen verschiedene Funktionen bereit, die Kernfunktionalität ist aber wohl das bereitstellen von Ressourcen einer Webapp, selbst dann, wenn das Gerät offline ist. Im Gegensatz zum sehr deklarativen Ansatz des AppCaches muss man dabei aber alles selbst machen. Dazu erstellt man eine Javascript-Datei die verschiedene Event-Listener implementiert. Der Clou ist dabei, das jedesmal wenn die zum Service Worker gehörende App auf das Internet zugreifen möchte, dieser Request an den Service Worker übergeben wird anstatt ihn direkt auszuführen. Dazu wird durch den Browser ein Fetch-Event ausgelöst dass dann im Service Worker verarbeitet wird. Dabei kann der Request verändert werden, mit vorhandenen Caches abgeglichen oder sonstige Aktivitäten durchgeführt werden.
Um das Ziel der Offlineverfügbarkeit zu gewährleisten gibt zusätzlich eine neue Art von Cache. Der Cache Storage ist ein Key-Value-Store. Die Keys sind dabei Request-Objekte und die Werte die zugehörigen Responses. Eine ganze Reihe von Möglichkeiten Anforderungen an die Verfügbarkeit und Aktualität zu genügen gibt es im Offline Cookebook von Jake Archibald.
Oben hatte ich ja gesagt das ein Service Worker verschiedene Eventlistener implementiert. Einige hängen mit dem Lebenszyklus von Workern zusammen. In diesen kann man schon mal die Caches vorbeifüllen oder Updates von einer IndexDB machen. Auch zum Lebenszyklus hat Jake Archibald wieder gute Informationen zusammengestellt.
Wenn man den Bedarf hat zwischen der Webseite und dem Service Worker (oder umgekehrt) Nachrichten auszutauschen kann man hierfür Message Chanels nutzen. Wie das geht hat diesmal Craig Russell in seinem Artikel How to Send Messages Between Service Workers and Clients aufgeschrieben.
Ein Feature von Service Workern das ich nicht nutze: Sie ermöglichen es Push Notifications zu empfangen. Wer hier mehr wissen will muss aber selber recherchieren.
Alles in allem bin ich recht zufrieden. Lediglich Firefox unter Android vermisst mir etwas den Tag. Seit ich den Service Worker einsetze zeigt mir dieser Browser keine Favicons mehr an. Bislang konnte ich auch noch nicht herausfinden was das soll.
Entgegen meiner sonstigen Gewohnheit enthält dieser Artikel keinen Code. Wenn du möchtest das ich einen bestimmten Aspekt näher beleuchte, schreib mir doch bitte einen Kommentar oder eine e-Mail. Wer einen Blick auf meinen konkreten Service Worker werfen will kann das auf Github tun.
]]>Spaß beiseite! Das Buch von Uwe Post ist ein eingängiges Lesevergnügen für alle die Software entwickeln. Dank des luftigen, lockeren Schreibstils liest es sich recht schnell weg. Inhaltlich geht das Werk auf allerlei Techniken und Tools die in Java- und C#-Projekten die Entstehung einer Software unterstützen: Versionskontrolle, Projektmanagement, Teamkommunikation, Testing, Dokumentation, Continuous Integration. Vieles davon wird der Programmierer von Heute hoffentlich schon kennen. Für mich war das Buch allerdings nicht aufgrund seiner einzelnen Themen interessant sondern aufgrund der kompakten Auffrischung des gesamten Ökosystems das zur Entstehung von guter Software beiträgt.
Die eben beschriebenen Themen werden ergänzt durch „echte“ Programmierthemen wie Entwurfsmuster, Refactoring und Multithreading. Alles zusammen hat ganz klar seinen Schwerpunkt auf die Java-Welt. Für alle .NET-Fans wird auch noch auf die Eigenheiten dieser Plattform und deren Tools eingegangen. Für andere Umgebungen muss der Leser den Inhalt aber selber adaptieren.
Alles in allem bin ich sehr zufrieden mit dem Buch. Um allerdings noch etwas Kritik zu üben hier zwei Punkte: Bei manchen der Anmerkungen zu den eingestreuten Anekdoten viel es mir schwer zu entscheiden ob diese sarkastisch gemeint sind oder nicht – ein Nachteil der Schriftform. Bei der Vorstellung des Toolings zum Continous Integration wird für Java nur auf Jenkins eingegangen, im Gegensatz zu allen anderen Kapitel wo eigentlich immer auch eine Alternative besprochen wird. Ob dies daran liegt das Jenkins allem anderen Haushoch überlegen ist oder der Author keine Erfahrung über Jenkins hinaus aufweisen kann wird nicht klar.
Ich beziehe mich übrigens auf die gedruckte Version, zur Aufbereitung als eBook kann ich nichts sagen. Die Form (Zeilenabstand, Schriftgröße und so) ist, passend zum lockeren Schreibstil des Autors, recht luftig und leicht lesbar. In der Marginalspalte gibt es verstreut Stichwörter über den nebenstehenden Absätzen. Die hätte es meines Erachtens nicht benötigt, sie stören aber den Lesefluss nicht.
]]>