Spring naar hoofdtekst

FWieP.nl redress 2021

Geplaatst op door .
Laatste aanpassing op .

Inleiding

In een eerder artikel beschreef ik een radicale ombouw van mijn website. Onder meer de overstap van Bootstrap 3 naar 4 passeerde de revue. Dit keer is de verandering meer aan de oppervlakte; de website heeft onder meer een nieuw kleurenthema gekregen: donker groen in plaats van fel rood.

WAVE

De directe aanleiding voor deze facelift was mijn ontdekking van WAVE; een hulpmiddel voor het evalueren van toegankelijkheid van websites en -applicaties. Plotseling kon ik met één druk op de knop (Ctrl + Shift + U) in Firefox zien dat mijn hele site te kampen had met behoorlijke contrastfouten. En dat, terwijl toegankelijkheid voor mij als slechtziende computergebruiker heel belangrijk is!

Regel voor regel

Toen ik eenmaal de broncode van mijn site onderhanden had, besloot ik dan maar ook letterlijke elke regel na te lopen. Als mij er ook maar het minste of geringste aan opviel of stoorde, zou ik het verhelpen.

RuntimeData

Het komt vaak voor dat je als programmeur steeds weer dezelfde code, of dezelfde data nodig hebt. Met object georiënteerd programmeren heb ik de code zo ver mogelijk gecentraliseerd. Toch bleven er nog steeds in PHP globale variabelen over die ik in de verschillende pagina's of zelfs klassen gebruikte. Dat moest anders!

Met de klasse RuntimeData, zie hieronder, kan ik door de gehele site of applicatie van dezelfde data gebruik maken. Deze wordt maar één keer geinitialiseerd en is bovendien ook nog eens typesafe, het type van de variabele staat bij het programmeren vast. Dit patroon heet singleton.

final class RuntimeData
{
    public $fileBase;
    // ...

    private static $_instance;

    public static function g() : self
    {
        if (is_null(self::$_instance)) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    private function __construct()
    {
        $this->fileBase = dirname($_SERVER['SCRIPT_FILENAME']);
        // ...
    }
}

// Somwhere in the application:
echo RuntimeData::g()->fileBase;

Limburgs wereldwijd

Onder toegankelijkheid valt ook het consequent opgeven van de taal van de content. Mijn gehele site is gedeclareerd als Nederlands (lang="nl"). De Engelstalige liedteksten zijn gekenmerkt met lang="en", de Duitse met lang="de". Alleen de Limburgse teksten kon ik tot voor kort niet echt specificeren. Als tijdelijke oplossing vond ik ergens het lang="und"- attribuut: undefined oftewel onbekend. Wat blijkt? Limburgs is wereldwijd bekend met een eigen taal-attribuut: lang="li".

<div lang="li">
Lot mich dich 'ns vertelle wie sjtolz ich bin dat 't sogar in XML ing
meugelikheed git um 't Limburgs es zoedanig aa te geave. Sjun, wa?
</div>

Anti-cache

Bij het bezoeken van een website of het gebruik van een webapplicatie worden de bestanden daarvoor vaak één keer aangevraagd en daarna door de browser opgeslagen voor later, oftewel gecached. Dat verkort de tijd die de browser nodig heeft om de volgende pagina op te bouwen.

In sommige gevallen, maar zeker tijdens het ontwikkelen, is dit cachen een doorn in het oog van zowel de ontwikkelaar als de gebruiker. De browser moet er bij een update op de server namelijk handmatig van worden overtuigd om de nieuw(st)e versie van het JavaScript, stylesheet of favicon te laden. De betreffende toetscombinatie is vaak Ctrl + F5.

Om dit probleem te omzeilen maak ik sinds kort gebruik van een querystring variabele. Deze wordt afgeleid van de inhoud van het bestand in kwestie. Als het bestand wijzigt, wijzigt ook de variabele en zal de browser het bestand opnieuw aanvragen.

<link rel="stylesheet" href="css/app.min.css?v=b145e1ca..." />

Integriteit

Als je er voor kiest om scripts of stylesheets van een andere site in jouw eigen pagina's te gebruiken, moet je er maar op vertrouwen dat de beheerder van die site zijn/haar zaakjes op orde heeft en de bestanden niet opeens aanpast. Wat als daar wordt ingebroken en de scripts door een crimineel worden voorzien van een achterdeur die daarna jouw bezoekers bespioneert?

Om dit scenario te voorkomen, kun je met het integrity-attribuut een cryptografische hash opgeven waarmee de browser het gedownloade bestand controleert. Komt de hash niet overeen, wordt het bestand verworpen en niet gebruikt of uitgevoerd.

Ondanks dat ik uit principe alle bestanden op fwiep.nl zelf host, vond ik het toch een mooi idee om ook mijn scripts en stylesheets van deze extra beveiliging te voorzien. Aldus geschiedde:

<link rel="stylesheet" href="css/app.css?v=..." integrity="sha384-..." />
<script src="js/app.js?v=..." integrity="sha384-..."></script>

Optimalisatie

De betreffende hashes zou ik natuurlijk bij elk bezoek door PHP kunnen laten berekenen, maar ik zag al meteen dat het efficiënter zou zijn om ze als het ware offline te berekenen en daarna als constantes te gebruiken. Aldus ging ik met md5sum en openssl handmatig aan de slag.

md5sum css/app.css | cut -s -d ' ' -f 1;
openssl dgst -sha384 -binary css/app.css | openssl base64 -A;

Daarna kon ik de constantes als volgt toepassen:

// config.php
define('CSS_MD5', 'b145e1ca9107867e696de9b1b50b8938');
define('CSS_SHA', 'sha384-4lbQ/u6i562Ka/j+7UG5+q...');

// page.php
print '<link rel="stylesheet" href="css/app.css?v='.MD5_CSS.'" integrity="'.SHA_CSS.'" />';

Automatisering

Er ontbrak nog één schakel in de volledige automatisering van dit stuk code. Als ik in VisualStudio Code, mijn ontwikkelomgeving, een aanpassing maakte en de bestanden met een buildtask opnieuw liet wegschrijven, moesten de hashes nog opnieuw worden bepaald. Ik schreef het volgende shell-script en nam het op in de bouwtaak:

# Generate the MD5 filehash for the CSS file
MD5_CSS="$( md5sum css/app.css | cut -s -d ' ' -f 1 )";

# Replace the value of the defined constant with the new hash
sed -i -e "s!define('MD5_CSS', '[^']*');!define('MD5_CSS', '${MD5_CSS}');!" "config.php"

# Generate the cryptographic filehash for the CSS file
SHA_CSS="$( openssl dgst -sha384 -binary css/app.css | openssl base64 -A )";

# Replace the value of the defined constant with the new hash
sed -i -e "s!define('SHA_CSS', '[^']*');!define('SHA_CSS', 'sha384-${SHA_CSS}');!" "config.php"

HighliteJS

Al vanaf het begin van dit weblog maak ik gebruik van Highlite.js. Met een extra stuk CSS en JavaScript worden lappen broncode op de pagina's voorzien van de betreffende syntax kleuren. Dit bevordert de leesbaarheid enorm. Tijdens de recente ombouw wilde ik dit project graag volledig integreren in mijn bestaande code. Jammer genoeg vond ik de honderden kilobytes extra JavaScript te veel van het goede voor mijn redelijk slanke app.min.js.

Dus ging ik op zoek naar een mogelijkheid om het inkleuren al op de server te doen. Ik vond highlight.php van Geert Bergman en was blij verrast. Samen met de MarkdownExtra parser van Michel Fortin kon ik zo elk codeblok voorzien van de passende opmaak.

$hl = new Highlight\Highlighter();
$me = new Michelf\MarkdownExtra();

$me->code_block_content_func = function ($code, $language) use ($hl) {
    try {
        return $hl->highlight($language, $code)->value;
    } catch (\DomainException $ex) {
        return $hl->highlight('plaintext', $code)->value;
    }
};

Ik hoefde alleen nog een passende stylesheet te kiezen en deze op te nemen in mijn SCSS bronbestand:

/* main.scss */
@import "../vendor/scrivo/highlight.php/styles/a11y-light";

Shariff en FontAwesome

Bij de lancering van mijn nieuwste demo-EP wilde ik graag elke bezoeker de mogelijkheid geven om mijn muziek eenvoudig te delen. Met de hulp van c't Shariff was dit op een open source en privacyvriendelijke manier mogelijk. Eén van de afhankelijkheden van dit project was het gebruik van de FontAwesome lettertypen. Opeens had ik de beschikking over een keur aan welbekende icoontjes, logo's en tekens.

Aldus maakte ik daar dankbaar gebruik van voor, onder andere, het aangeven of een link naar een externe site verwijst. Ook kon ik links naar een PDF-download als zodanig kenmerken. Handig!

$(document).ready(function()
{
  // Make external links open in a new window, show "extern" indicator
  $('a[rel=external], a[href^="https://"], a[href^="http://"]')

    // Exclude alternate URLs
    .not('a[rel="alternate"]')

    // Set to open in a new window/tab
    .attr('target', '_blank')

    // and append a visual and text-only indicator
    .append(
    '<span class="visually-hidden"> (extern)</span>' +
    '<span class="ms-1 fas fa-external-link-alt"></span>'
  );

  // Show a PDF-icon (and a screen reader note) to PDF-links
  $('a[href$="\.pdf"]').each(function(ix,a) {
    $(a).append(
      '<span class="visually-hidden"> (PDF)</span>'+
      '<span class="fas fa-file-pdf ms-1"></span>'
    );
  });
});
Terug naar boven

Inhoudsopgave

Delen

Met de deel-knop van uw browser, of met onderstaande koppelingen deelt u deze pagina via sociale media of e-mail.

Atom-feed van FWiePs weblog

Artikelen


Categorieën

Doorzoek de onderstaande categorieën om de lijst met artikelen te filteren.


Terug naar boven