Archiv der Kategorie: Allgemein

Stichwortverzeichnis: der Shortcode

In WordPress einen Shortcode zu erzeugen ist recht einfach. Man fügt einfach folgenden Code ein:

//**********************Beginn Shortcode erzeugen
add_shortcode( 'shortcode_name', 'shortcode_handler_function' );

function shortcode_handler_function(){
	echo "Ich bin dein Shortcode";
	}

//***************End Shortcode erzeugen

Jetzt an beliebiger Stelle in einem Beitrag oder auf einer Seite das Kürzel [shortcode_name] einfügen und hoffen dass es funkt… ich hab da öfter eine JSON-Fehlermeldung, die sich durch ab- und wieder anmelden oder Plugin neu aktivieren beheben liess, aber nicht immer. WordPress kann auch sehr zickig sein wenn man versucht einen Shortcode umzubenennen, da gibts gern mal chaotische Fehlermeldungen. Sei’s drum.

Ich habe mich dafür entschieden ein eigenes Plugin für die Erzeugung des Shortcodes zu erstellen, dann hab ich den Code sauber getrennt und tu mir leichter mit dem Fehlerkorrigieren. Der Shortcode soll die CSV-Datei mit den Stichworten zeilenweise einlesen und in ein Array schreiben, daraus basteln wir dann das alfabetisch sortierte Stichwortverzeichnis mit den Links. Wie ich das mit der Unterseite löse weiß ich noch nicht, da heißts design as you go 🙂

Aber jetzt wollen wir mal.

Zuerst mal müssen wir unsere CSV-Datei einlesen und den Inhalt auf eine Array-Variable legen. Das geht zum Beispiel so:

//Variable für Liste bereitstellen
	$listenarray = array();	
	
	//csv-Datei einlesen
	$listenarray=csv_einlesen();

Die Funktion csv_einlesen sieht dann so aus:

function csv_einlesen(){
	
	//Verzeichnis ermitteln
	$dir = plugin_dir_path( __FILE__ );
	$aktVerzeichnis = $dir;
	
	//Der Name der CSV-Datei ist fest verdrahtet, das wäre nice to have anders
	$neuName = $aktVerzeichnis.'stichwortliste.csv';
	echo "Lese aus Datei ".$neuName."</br>";
	
	//Array anlegen
	$aktListe = array();
	
	
// Datei öffnen, $handle ist der Dateizeiger
$handle = fopen ($neuName,'r');
//Solange nicht EOF erreicht, 1000 ist die maximale Zeilenlänge
while (($csv_array = fgetcsv ($handle, 1000)) !== FALSE ) {

  foreach ($csv_array as $index) {
    //echo $index . '<br>';
	//Hier kommt der Knackpunkt: Neues Stichwort in Array schreiben
		  //***********************************
		  array_push($aktListe, $index);
		  //***********************************
  }
}

fclose($handle);

//Debug-Ausgabe
echo "Array eingelesen, Anzahl der Wörter: ".sizeof($aktListe)."</br>";


return $aktListe;	
	
}//Ende csv_einlesen

Das ist jetzt nicht weiter tragisch, der Witz ist dass das Array mit den eingelesenen Stichwörtern als Rückgabewert der Funktion dient. Damit können wir weiterarbeiten.

Als nächstes bauen wir ein schönes, alfabetisch geordnetes Stichwortverzeichnis auf, dazu brauchen wir zunächst mal ein Array mit allen Buchstaben des Alphabets. Durch das steppen wir durch und geben die Stichworte aus, wenn sie mit dem richtigen Buchstaben anfangen.

//Stichworte Alfabetisch gruppiert ausgeben
	
	//Array mit Alfabet erzeugen
	$alphas = range('A', 'Z');

	//Durch alfabet durchsteppen
	foreach($alphas as $letter){
		
		
		echo "<h2>Buchstabe ".$letter."</h2></br>";
		
		//Nur einfügen, wenn mit dem richtigen Buchstaben anfängt
		foreach($listenarray as $einwort)
		{
			
			$hilf = substr($einwort,0,1);
			
			
			if ($hilf == $letter){
						
				echo $einwort."</br>";
//************************************************To Do: Link erzeugen
			}
			
		}
		
		
	}//ende for each buchstabe

Dann hörts aber auf. Ich suche noch nach einer eleganten Möglichkeit, die Liste als Links zu formatieren, die dann eine Unterseite aufmachen. Auf der Unterseite sollen natürlich alle Rezepte zum gewählten Stichwort angezeigt werden. Das funktioniert, solange ich eine statische Seite generiere und deren URL fest verdrahtet mitgebe, der Code für den Link sieht dann so aus:

echo '<a href="', "http://localhost:819/inselfisch-kochbuch/alle-eintraege-zu-stichwort/?stichwort=$einwort'",' ",">', $einwort, '</a></br>';	

Das ist jetzt aber nicht der Weisheit letzter Schluss, fest verdrahtete Dateinamen sind ganz, ganz schlechter Stil. Da muss ich mir was anderes einfallen lassen. Ich hab jetzt mal eine Anfrage im deutschen WordPress-Forum gestartet, mal schauen ob da was dabei rauskommt. Einstweilen: einen frischen Kaffee!

Intermezzo: warum ich so eine schlechte Programmiererin bin

Ich programmiere schon seit über 40 Jahren, und das meistens hauptberuflich. Ich habe in meinem Leben schon -zig Projekte kommen, gehen und sterben sehen und schon die tollsten Abenteuer erlebt. Aber eins hab ich noch nie gemocht: Fehlerbehandlungsroutinen (grusel)

Natürlich macht es Sinn, nach dem Versuch sich mit einer externen Datenbank zu verbinden abzuchecken, ob die Verbindung OK ist. Es macht Sinn zu überprüfen, ob eine weggeschriebene Datei existiert und den erhofften Inhalt hat. Es macht Sinn bei Lösch- und Aktualisierungsoperationen einen doppelten Boden einzubauen (..wollen Sie wirklich? Ja/Nein)

D’accord? Tscha, aber ich machs nicht. Seit ich im Ruhestand bin und eigentlich nur noch zum Vergnügen programmiere, erst recht nicht. Schließlich kann ich meistens mit auftretenden Fehlermeldungen etwas anfangen und korrigiere dann meinen Sourcecode entsprechend. Das kann ich mir deshalb erlauben, weil meine Projekte meistens Standalone-Routinen sind, zum Beispiel WordPress-Plugins oder VBA-Module. Mein Code ist selten länger als zwei, drei DIN A 4 Seiten und somit noch recht überschaubar, das geht schon. Guter Programmierstil ist es nicht, schon gar nicht wenn man in grösseren Projekten arbeitet – tu ich aber nicht (mehr).

Ich werde aber den Teufel tun und meine schlechten Programme in echt einsetzen, etwa auf meinen Live-Blogs (Inselfisch.Kochbuch, dieser Blog zum schwarzen Pinguin). Nee, die laufen auf meinem lokalen Xampp-Webserver auf den Test-Installationen oder in einer Access-Datenbank oder einer Exceltabelle, und ich lerne viel dabei. Und sei es, daß ein Programm so instabil läuft, daß es für den Livebetrieb gänzlich ungeeignet ist. That’s life. Mir macht Programmieren trotzdem Spaß! 🙂

Und wer bei mir was abkupfern möchte, kann das gerne tun, muss aber dann seine eigenen Fehlerbehandlungsroutinen einbauen. Ohne gehts im Echtbetrieb nun mal nicht.

Darf ich vorstellen: Stichwort Plugin Teil 1, CSV-Datei erzeugen

Da ich die ganze Mechanik schon mal programmiert habe, in Access mit Visual Basic, hab ich mir relativ leicht getan es auch in PHP zu lösen. Was gar nicht schön war: bei komplexeren Datenbankoperationen ist mir x-mal der Webserver abgeraucht. Deswegen hab ich dann die Notbremse gezogen und bin auf eine CSV-Datei ausgewichen. Nicht die schlechteste Lösung, was Stabilität und Performance angeht.

Was hab ich gemacht? Ich geh da mal im Schnelldurchlauf durch, haben wir alles schon mal so oder in ähnlicher Form gehabt. Trotzdem, das eine oder andere ist vielleicht gut zu wissen.

Also,, zunächst wird ein Plugin mit einem Eintrag für das Admin-Menü erstellt. Dazu legt man eine Datei an, die folgendermassen aussieht:

/*
Plugin Name: Stichworttabelle
Description: Erzeugt eine neue Stichworttabelle aus den Titeln der Beiträge (post_title)
Author: Evi Leu
Version: 0.1
*/
    add_action('admin_menu', 'stichworttabelle_plugin_setup_menu');
	
	function stichworttabelle_plugin_setup_menu(){
    add_menu_page( 'Stichworttabelle', 'Stichworttabelle Plugin', 'manage_options', 'stichworttabelle', 'stichworttabelle_init' );
}
 
function stichworttabelle_init(){... HIER GEHTS MIT DER MAIN FUNCTION LOS

Diese Datei kommt in ein eigenes Unterverzeichnis „Stichworttabelle“ im Plugin-Verzeichnis deiner WP-Installation und heißt Stichworttabelle.php. Sie kann jetzt in der Liste der installierten Plugins aktiviert werden. Sie tut zunächst mal nichts ausser einen Eintrag im Admin-Menü zu erzeugen, der heißt „Stichworttabelle Plugin“ und ist erstmal noch eine leere Seite.

Die Menüseite wird in der Funktion function stichworttabelle_init() mit Leben gefüllt, erst kommt ein bisschen Infotex, dann wird der Name der Datenbank ermittelt und ausgegeben:

echo "<h1>Stichworttabelle neu erstellen</h1>";
	echo "Das Plugin hat zwei Funktionalitäten: </br></br>
	1. Diesen Admin-Menüpunkt Stichworttabelle Plugin, in dem die Stichworttabelle neu aufgebaut werden kann.</br>
	Aus Performancegründe und wegen der Runtime-Stabilität wurde die Stichwortbasis in eine externe CSV-Datei ausgelagert. Diese wird neu erstellt, falls sie nicht schon vorhanden ist. Falls sie schon vorhanden sein sollte, wird sie überschrieben. Man kann die Datei beliebig oft neu erzeugen, z.B. wenn es grössere Mengen neuer Beiträge gibt.</br></br>
	
	2. einen Shortcode [stichwortverzeichnis]. der an beliebiger Stelle in einem Beitrag oder einer Seite eingesetzt werden kann und dort ein Stichwortverzeichnis erzeugt.</br></br>";
	global $wpdb;
	
	//Datenbankname ermitteln
	$mydatabase=$wpdb->dbname;
	echo "Sie arbeiten auf der Datenbank: ".$mydatabase."</br>";
	echo "Stichworte werden aus den Titeln ihrer Beiträge erzeugt</br>";

Dann kommt ein kleines Formular, das aus genau einem Button besteht:

//***************Begin Formular
//Formular mit Button
//"
// Stichwortliste Datei neu erzeugen startet die array-Erzeugung fuellen und befüllt die Datei stichwortliste.csv wieder

echo "<form action = '#' method = 'post'>";
	
	echo "<input type='submit' id='el_button2' name='ButtonFuellen' value='Stichwortliste Datei neu erzeugen'>";
	echo "</form>";
	
	if (isset($_POST['ButtonFuellen'])){
			
			return tabelle_fuellen();
		}
//*****************End Formular

Wenn auf den Knopf gedrückt wird, wird die Funktion tabelle_fuellen aufgerufen. Jetzt wirds interessant:

function tabelle_fuellen(){
	
	$neuesArray=array_erzeugen();
	erzeuge_csv($neuesArray);
}

Die Variable $neuesArray wird mit Hilfe der Funktion array_erzeugen() befüllt. Diese erstellt eine Stichwortliste aus den Titeln aller Beiträge in der Tabelle wp_posts, dazu gleich mehr.

Dann wird die Funktion erzeuge_csv aufgerufen, sie kriegt als Parameter unser Array mit und schreibt die Einträge zeilenweise in eine Datei.

Frischauf, wir sehen uns die Funktion array_erzeugen() mal näher an. Der erste Teil mit den nötigen MySQL-Abfragen sieht so aus:

global $wpdb;

//Beginn Originalcode
$table_name = $wpdb->prefix.'posts';

	//Datensätze zählen & Ausgabe Anzahl
	$count_query = "select count(*) from $table_name where post_status='publish' and post_type = 'post'";
	$num = $wpdb->get_var($count_query);
	echo $num."&nbsp Beiträge gefunden</br>";
	
//******************

//Alle Datensätze vom Typ post und published ausgeben
$alleposts = $wpdb->get_results( "SELECT * FROM ".$table_name."
								where post_status='publish' and post_type = 'post' order by post_title");


Das übliche Spiel wenn man die veröffentlichten Beiträge ausgeben will, man braucht in der Where-Klausel die Bedingung post_status=’publish‘ and post_type = ‚post‘. Und zugegeben, man könnte statt select * auch select post_title verwenden, das fällt mir erst jetzt auf.

Jetzt stecken alle veröffentlichten Beiträge als Array in der Variablen $alleposts. Durch dieses Array steppe ich jetzt mit foreach durch und nehme mir die einzelnen Einträge vor, die werden mit Hilfe der Funktion explode() am Leerzeichen gesplittet, mit preg_replace() von Sonderzeichen bereinigt und mit ctype_upper auf Groß/Kleinschreibung überprüft, ich nehme nur die groß geschriebenen Einträge. Das ist willkürlich festgelegt, produziert aber eine sehr brauchbare Stichwortliste. Schließlich wird der gefundene Eintrag mit array_push() in die Variable $stichwortliste weggeschrieben.

$stichwortliste = array(); 
//Durch alle gefundenen Datensätze durchsteppen
$zaehler = 0;
foreach ( $alleposts as $einpost ) 
{ 
  //ersten gefundenen Titel in array aufsplitten
  $liste = explode(" ", $einpost->post_title);
  
  //Durch das Array durchsteppen
   foreach ($liste as $einwort)
  {
	  //Prüfen, ob Wort groß geschrieben ist
	  $wortanfang = substr($einwort,0,1);
	  
	  //Sonderzeichen entfernen (nach Bedarf editieren)
	  $einwort = preg_replace('/[0-9\@\.\;\" "\(\)\:\?\!\,]+/', '', $einwort);
	  
	  //nur ausgeben wenn Groß geschrieben
	  if (ctype_upper($wortanfang)){
		 
		  $zaehler = $zaehler+1;
		  
		  //Hier kommt der Knackpunkt: Neues Stichwort in Array schreiben
		  //***********************************
		  array_push($stichwortliste, $einwort);
		  //***********************************
	  }// ende von ctype_upper
  }// ende von liste as einwort

  
}//ende von alleposts as einpost und array befüllen

Es folgt noch ein bisschen Kosmetik, und ganz am Ende gibt unsere Funktion das gebrauchsfertige Array zurück:

//Dubletten entfernen
$stichwortliste= array_unique($stichwortliste);

//Array sortieren
sort($stichwortliste);

//Ausgabe Anzahlen erzeugter Stichwörter
echo "Anzahl Stichwörter in den Rohdaten: ".$zaehler."</br>";
echo "Grösse des sortierten und Dubletten-bereinigten Arrays: ".count($stichwortliste)."</br>";
echo "<h2>Erzeuge neue Stichwortliste aus der Tabelle: ".$table_name."</h2>";

return $stichwortliste;
}// ende array erzeugen_function

Noch alle mit mir beieinander? Fehlt noch was? Ach ja, die Erzeugung der CSV-Datei mit Hilfe der Funktion erzeuge_csv(), damit halte ich mich jetzt nicht lange auf, die ist einigermassen selbsterklärend:

function erzeuge_csv($liste){
	
	global $wpdb;
	
	echo "Ich erzeuge jetzt ein csv: ";
	
	//***************
	// Verzeichis des aktuellen Plugins ermitteln
	$dir = plugin_dir_path( __FILE__ );
	$aktVerzeichnis = $dir;
	//Dateiname fest verdrahtet	
	$fileName = $aktVerzeichnis.'stichwortliste.csv';
	echo $fileName."</br>";
	
if(file_exists($fileName)){
	echo "Die alte Datei wird überschrieben</br>";
	}
    
	//Gnadenlos überschreiben, der Parameter 'w' ersetzt den alten Dateiinhalt
	
    $csvFile = fopen($fileName,'w');
    $head = ["Wort"];
    fputcsv($csvFile,$head);

// Variable mit den Listeneinträgen befüllen
foreach ($liste as $einwort){
$data = [
    ["$einwort"],
    
];

//Durch alle data-Einträge durchsteppen und in Datei schreiben

foreach($data as $row){
    fputcsv($csvFile,$row);
}
}
fclose($csvFile);

 //Debug-Ausgabe aller Stichworte
$anzahl = sizeof($liste);
echo "<h2>Testausgabe: ".$anzahl." Stichwörter erzeugt</h2>";

foreach ($liste as $stichwort){
	
	echo $stichwort."</br>";
	
}
		
}// Ende function erzeuge csv
//*****************************************

Falls die Datei nicht existiert, wird sie neu angelegt. Falls sie schon existiert, wird der Inhalt überschrieben. So das wars. Jetzt einen Kaffee und sacken lassen. Und dann Hurra auf zu neuen Ufern, jetzt kommt der Teil mit dem Shortcode. Aber dazu gibts einen neuen Beitrag.

Da knarzt es im Gebälk: ich brauche einen PAP für mein Stichwortverzeichnis

Also, ich hab ja jetzt schon ein paar Tage Gehirnschmalz in das Stichwortverzeichnis investiert, und stelle fest dass die Sache schon hübsch komplex wird. Vor allem muss ich mir Gedanken machen, wer was wann macht (machen darf) und was wohin gehört. Ich mach mal ein Brainstorming:

  • es soll ein WordPress-Plugin (PHP) werden, ich möchte ohne Datenimport/Export in MYSQL arbeiten
  • Auf der WordPress-Adminseite soll es einen neuen Menüpunkt geben: „Stichworttabelle neu erstellen“ . Hier soll erst eine Sicherung der alten Stichworttabelle (falls vorhanden) angelegt werden, dann die Tabelle quelle erst geleert und dann mit der aktuell aus der Tabelle wp_posts (Feld wp_title) neu erstellten Stichwortliste neu befüllt werden.
  • Es soll ein Shortcode mitgeliefert werden [Stichwortverzeichnis], der an beliebiger Stelle auf einer Seite oder in einem Beitrag eingefügt werden kann und der da ein komplettes Stichwortverzeichnis (mit Unterseite) generiert.
  • Es soll eine Positiv- und Negativliste geben, für Wörter die auf jeden Fall/auf keinen Fall im Stichwortverzeichnis auftauchen sollen (nice to have)

Ich seh schon, ich muss mich erst mal wieder mit dem good old WordPress auseinandersetzen, das ist verdammt lang her dass ich was mit Plugins und Admin-Menüs gemacht habe. OK, ich geh mal googlen… bis dann!

Update am Montagmittag: nachdem mir der Webserver noch einige Male unter lautem Jubel abgerauscht ist, schmeisse ich den PAP nochmal über den Haufen. Anscheinend sind die vielen PHP-gesteuerten MySQL-Kommandos zuviel für das gute alte WordPress. Ich machs jetzt ganz anders, ich lege die Stichwörter in eine CSV-Datei. Die sollte man eigentlich gefahrlos wieder auslesen können – ich bin da recht vorsichtig geworden.

Update am Montagabend: Das mit dem CSV war der Schlüssel zum Erfolg, jetzt läuft die ganze Chose stabil. Die Erzeugung einer Stichwortdatei aus den Beitragstiteln der wp_posts läuft einwandfrei, und dauert nicht mal lang. Morgen bastel ich dann den Shortcode, der eben jene CSV-Datei wieder ausliest und das Stichwortverzeichnis an beliebiger Stelle in einem Beitrag oder auf einer Seite aufbaut. Mal sehen ob das ohne grössere Unfälle funkt. Hach, Programmieren ist so eine kurzweilige Sache! 😉

Stichwortverzeichnis: frisch gewagt in PHP und WordPress

Ich hab mich so über die unzuverlässige Asc()-Funktion in Access geärgert, jetzt hab ich mir die ganze Mechanik der Stichwort-Extraktion nochmal in PHP angetan, das funktioniert wesentlich zuverlässiger und erspart mir ausserdem den ständigen Datenimport/Export.

Praktisch gelöst wird die ganze Sache erst einmal über einen Shortcode, da musste ich jetzt schon nochmal überlegen wie das genau ging, ist lange her. Und wie ich so in $wpdb-Nostalgie schwelge, ist mir ein anderer Dreh eingefallen, wie ich die Stichwörterliste aus den Post-Titeln extrahieren kann, nämlich völlig dynamisch mithilfe von Arrays. Bei einigen Hundert Wörtern geht das noch sehr locker, da brauche ich mir wegen der Performance noch keine Gedanken zu machen…. dachte ich zuerst mal.

Nachdem mir aber ein paar mal hintereinander der lokale Webserver abgeraucht ist bei dem Versuch, eine dynamische Stichwortliste mit Links zu erzeugen, habe ich mich für eine Hybridlösung entschieden. Ich werde die Erzeugung der Quelldaten für das Stichwortverzeichnis doch über eine Tabelle abwickeln, das ist der erste Schritt. Dafür baue ich mir nach Erzeugung und Bereinigung des Arrays eine kleine MySQL-Abfrage, die die vorhandene Tabelle „quelle“ zuerst ausleert und dann neu befüllt. Das wäre natürlich nett, wenn man das über eine Art Benutzerführung abwickeln könnte, aber ich schau erstmal dass ich es überhaupt zum Laufen kriege. Dann kann ich meinen bereits vorhandenen Shortcode zur Erzeugung des Stichwortverzeichnisses aus der Tabelle quelle wiederverwenden, der ist nämlich schon fertig und funkt einwandfrei. Ich hab da so eine Idee, dass man die Datenbasis immer wieder mal neu erstellen können soll, wenn zum Beispiel viele Beiträge dazugekommen sind. Da es für die Erzeugung des Stichwortverzeichnisses aus einer statischen Tabelle keinerlei großartige PHP-Konstrukte braucht, sollte die Performance und Stabilität auch deutlich verbessert werden. Dann hab ich auch noch so eine Idee mit Positiv/Negativliste, mein Plugin könnte lernfähig werden, da gehts schon hübsch in Richtung AI. Morgen ist auch noch ein Tag…

Ein ehrgeiziges Projekt: Stichwortverzeichnis

Meine beiden mit Abstand am häufigsten gebrauchten Kochbücher sind „Das Bayrische Kochbuch“ und „Joy of Cooking“. Und bei beiden ist das am Meisten genutzte Feature das Stichwortverzeichnis, da findet man alles! Es eignet sich auch hervorragend zum kreuz- und querschmökern, man findet dabei Rezepte die man sonst nie gesehen hätte. Langer Rede kurzer Sinn, ein Stichwortverzeichnis (Volltext-Index) wäre ein schickes Feature für mein Inselfisch-Kochbuch. Und dazu möchte ich nicht das Stichwort-Feature von WordPress nutzen, das ist mir viel zu umständlich zu bedienen und zu schlecht auszuwerten.

Dazu waren einige Vorüberlegungen nötig. Zunächst muss ich mal festlegen, woher ich die Stichwörter nehme. Ich habe in meinem Inselfisch-Kochbuch knappe 400 Rezepte, in denen relevanter Text in zwei Feldern steht, einmal in post_title und einmal in post_content. Der Titel ist meist zwischen 5 und 10 Wörter lang, der Content kann wesentlich länger sein, bis zu einer ganzen DIN A 4 Seite und mehr, also auf jeden Fall mehr als 100 Wörter. Ich möchte meine Stichwortbasis natürlich maschinell erzeugen, dazu muss ich irgendwie einzelne Wörter aus meiner Datenbasis extrahieren. Dazu muss ich meinen Quelltext aufsplitten (ob mit PHP oder VBA wird sich noch zeigen) und in eine Tabelle schreiben. Diese Tabelle (ich nenne sie mal Rohdaten) soll am Ende nur alle Stichwörter und eine ID (Autowert) enthalten. Man sieht schon: mit dem Content, das wird uferlos, das können leicht mehrere Zehntausend Wörter werden. Ich beschränke mich also auf die Wörter aus dem post_title, das ist besser zu handeln. Wir machen das mal als erste Annäherung und schauen uns dann an, was wir mit den extrahierten potentiellen Stichworten anfangen können.

Wie ich das in MS Access angehe: ich importiere mir die gesamte Tabelle wp_posts aus dem Original-Inselfischkochbuch über CSV-Export aus phpmyadmin. Dann schreibe ich mir eine Abfrage,, die nur den post_title enthält und als where-Klausel post_Type = post und post_status = publish bekommt, damit ich nur die echt veröffentlichten Rezepte in der Datenbasis habe. Die Abfrage heißt quelle und sieht so aus:

Screenshot quelle

265 Datensätze a drei bis zehn Wörter, damit kann man arbeiten. Ich lege ausserdem fest, dass ich nur Großgeschriebene Wörter im Stichwortverzeichnis haben möchte. Das ist willkürlich, aber doch recht sinnvoll, weil damit die ganzen kleinen Füllwörter rausfallen.

Dann gehts rund: Ich lege mir eine Tabelle namens ziel an, die nur zwei Felder hat: ID(AutoWert) und Wort(Text). Sie bleibt zunächst leer. Dann bastle ich mir ein VBA-Modul. Hier lege ich zwei Recordsets an, rstquelle das ist die Abfrage Quelle, und rstziel, das ist die Tabelle Ziel.

Jetzt gehe ich zum ersten Datensatz in rstquelle und lese mir den Inhalt des Feldes post_title ein. Dann verwende ich die VBA-Funktion split(), die zerlegt das Feld in einzelne Wörter, die in ein Array geschrieben werden.  Ich laufe durch dieses Array und prüfe zunächst, ob das Wort Groß oder klein geschrieben ist:

Asc(Left(liste(i), 1) >= 65) And (Asc(Left(liste(i), 1)) <= 90)

Mit dieser Funktion bin ich nicht so recht glücklich, weil sie unerklärlicherweise manche Wörter mit Kleinbuchstaben doch durchrutschen läßt, aber ich hab noch nichts besseres gefunden.

Dann werden noch evtl vorhandene Sonderzeichen entfernt, dazu gibts eine Funktion die Folgende Zeichenkette durchläuft:

Const strSonderzeichen As String = „.,:;#+’*?=)(/%$§!~\}][{“

Dann schreibe jeweils ein gefundenes, geputztes Wort in die Zieltabelle. Dann gehe ich zum nächsten Datensatz der Quelltabelle und wiederhole den Vorgang. das mache ich, bis EOF der Quelltabelle erreicht ist.

Hurra! 669 Datensätze, beim ersten Drüberschauen sieht es schon mal ganz gut aus. Noch die Dubletten ausblenden, das geht mit einer Abfrage mit Gruppierung ganz easy. Sortieren, man sieht noch ein bisschen Datenschmutz ganz oben, das bereinigt man per Hand.

abfrage quelle

Es bleiben 496 recht manierliche Stichworte übrig. Damit könnte ich jetzt schon nach MySQL und WordPress gehen und die Webseite aufbauen, aber ich halte mich noch ein wenig in Access auf und teste nochmal, mit der „wackeligen“ Asc-Funktion bin ich nicht zufrieden.

PHP ist tot? Mein Bankkonto hat nichts davon gemerkt…

So lautet, frei übersetzt, ein Zitat von Brandon Savage, PHP Developer und grosser Spaßvogel auf Twitter.

In der Tat, mit dem ganzen Brimborium um den neuen WordPress-Editor Gutenberg, der ja komplett in React.js programmiert wurde, ist es gerade wieder einmal angesagt PHP für tot zu erklären. Da hat mich ein Passus aus diesem Artikel bei Kinsta köstlichst amüsiert:

„For a humorous take on it, there’s this Reddit post where a job description wanted a React developer with 5 years of experience back in 2017, at which point React had only been around for ~4 years.“

Da wurde 2017 ein React Developer mit 5 Jahren Erfahrung gesucht, aber React war zu diesem Zeitpunkt erst ~4 Jahre auf dem Markt. Sowas nennt man Hype, und leider fallen viele Entscheider, die selbst selten Developer sind, auf so etwas herein und fällen Entscheidungen für oder gegen zu verwendende Programmiersprachen je nach dem was gerade angesagt ist. Und angesagt scheint zur Zeit nur Javascript und sämtliche angeschlossenen Derivate und Bibliotheken.

Das hindert aber PHP nicht daran, nach wie vor die erfolgreichste Programmiersprache im Internet zu sein. Ich zitiere mal noch ein paar Zahlen aus dem Kinsta-Artikel:

  • Im November 2017 berechneten W3Techs PHP auf 80,1 % der Webseiten als serverseitige Programmiersprache.
  • Im Juni 2018 waren es noch 79,6 %.
  • PHP wird heute (November 2018) von 78% aller Webseiten benutzt, die eine serverseitige Programmiersprache verwenden.
  • Wenn die Verwendung von PHP weiter in diesem Masse abnimmt, wird es noch ca. 25 Jahre dauern bis PHP unter die 50 % Marke kommt.

Das sieht jetzt aber wirklich nicht nach Zahlen vom Sterbebett aus, oder? Wenn man noch in Betracht zieht, dass sowohl WordPress (ca. 32% aller Webseiten laufen heute auf WordPress), als auch Wikipedia, als auch alle Drupal- und Joomla-Webseiten PHP-basiert sind, siehts auch nicht wirklich toter aus.

Und wenn in erhitzten Diskussionen heutzutage immer wieder gegen PHP argumentiert wird, aus welchen Gründen auch immer, da gefällt mir ein Zitat von Bjarne Stroustrup, dem Schöpfer von C++ ganz hervorragend:

„Es gibt nur zwei Arten von Programmiersprachen. Diejenigen, über die die Leute meckern, und diejenigen, die niemand benutzt.“

Das ist ein schönes Schlußwort, finde ich – und mache im nächsten Artikel wieder ein Tänzchen mit PHP, weil es einfach Spaß macht.

Ich bin Accessibility-Aktivistin

Diese Vokabel, im Original „Accessibility activist“ habe ich im Web aufgeklaubt, und sie passt mir wie Latsch auf Pantoffel. Aktivistin, das hat auch was Kämpferisches und Radikales, und damit kann ich mich durchaus identifizieren. Ich meine das ernst mit dem barrierefreien Webdesign, es ist mir ein echtes Anliegen, meine Kunden bei der barrierefreien Gestaltung ihrer Webauftritte behilflich zu sein. Das hat viele Facetten, von einer rein technischen Bedienbarkeit mit der Tastatur und dem Screenreader über die barrierefreie Strukturierung und klare Gliederung angebotener Texte bis hin zu einer klaren, barrierefrei verständlichen Sprache, das Anwendungsspektrum der Barrierefreieheit ist weitgefächert.

Und je mehr ich lerne, um so klarer wird mir auch dass ich noch viel zu lernen habe und wahrscheinlich noch auf Jahre hinaus täglich etwas Neues in Sachen Barrierefreiheit lernen kann. Trotzdem werde ich nie alles wissen können, dafür ist das Gebiet einfach zu umfangreich. Aber ich bin dran, und ich bin in Diskussionen zum Thema auch durchaus ein bisschen ein Radikalinsky, eben weil ich davon überzeugt bin,  dass Barrierefreiheit speziell im Internet nicht nur wichtig, sondern auch notwendig und überaus qualitätsfördernd ist. Ich leihe mir da mal den Slogan einer Teamkollegin:  „Accessibility – for a brighter, clearer Web!

Das habe ich mir auf die Fahne geschrieben, und damit ziehe ich in dem Kampf, aber nicht verbissen, sonder guten Mutes und guter Laune. Es gibt viel zu tun – legen wir los!

attacke

attacke

Barrierefreie Farbkontraste 2: der Sourcecode

OK jetzt gilts: man kann eine beliebige RGB-Farbe wählen und sich ausgeben lassen, welche der 140 benannten HTML-Farben einen guten (barrierefreien) Kontrast dazu liefert. Das kann man nicht nur für Schriften verwenden, sondern auch für Logos und Icons, die auf einen farbigen Hintergrund gesetzt werden sollen.

Voraussetzung ist eine Tabelle, in der die 140 Farben mit Color Group (Farbfamilie), Namen und Hex- und RGB-Notation gespeichert sind, ich zeig hier noch mal einen Ausschnitt:

mysql_farbtabelle

mysql_farbtabelle

Wie die Farben da reinkommen beschreibe ich jetzt hier nicht, das ging über ein Excel-Makro und ist letzendlich völlig wurst für die Funktionalität. Also, wir haben unsere Farbtabelle.

Jetzt brauchen wir eine Datenbankverbindung, die beschreibe ich hier auch nicht mehr extra. Dann muss wieder mal ein Formular her, für die Auswahl der Basisfarbe bietet sich der Input-Type Color an, bei dem man in den meisten modernen Browsern einen schicken Colorpicker angezeigt bekommt. Man könnte auch ein Eingabefeld für den Hex- oder RGB-Wert nehmen, das ist letztlich Geschmackssache. Also, los gehts, mit einer Datei namens farbnamen.php, die sieht zunächst so aus:

<!DOCTYPE html>
<html lang="de">
<style type="text/css">
h1,h2,h3{
    font-family:Arial;
}
</style>
<head>
<title>Colors Arbeitspapier</title>
</head>

<body>
<h1>Beispiel Contrast Colors mit HTML Named Colors</h1>

<form action="farbnamen.php" method="post">

      <h2>Bitte eine Farbe auswählen:</h2>
      <input type="color" name="farbe" value=""><br>
      <h2>Bitte eine Farbfamilie für die Kontrastfarbe wählen:</h2>
    <select name="farbfamilien">
        <option value="BLUE">Blau</option>
        <option value="GREEN">Grün</option>
        <option value="PURPLE">Violett</option>
        <option value="WHITE">Weiß</option>
        <option value="BROWN">Braun</option>
        <option value="YELLOW">Gelb</option>
        <option value="GRAY">Grau</option>
        <option value="RED">Rot</option>
        <option value="PINK">Pink</option>
        <option value="ORANGE">Orange</option>
      </select>
     <br><br>
    <input type="submit" name = "senden">
  
</form>

Das ist erstmal straightes HTML, zuerst kommt der Color Picker, dann das Dropdownfeld für unsere Farbfamilien, und das wars auch schon. Man könnte den Select auch mit einem Distinct aus der Datenbank füttern, das hab ich mir jetzt gespart und die 10 Einträge so reingeschrieben.

Los geht es wie immer wenn auf den Submit-Button geklickt wurde. Der Colorpicker liefert den Hex-Wert der gewählten Farbe, das Dropdownfeld den Namen der Farbfamilie. Den Hex-Wert zerlege ich in die einzelnen Komponenten, die brauche ich später um meine Funktion zur Berechnung der wahrgenommenen relativen Helligkeit damit zu füttern, dazu weiter unten mehr. Das Ganze sieht jetzt erstmal so aus:

startbildschirm

startbildschirm

Bei Klick auf das schwarze Farbkästchen poppt der Colorpicker auf, in Chrome beispielsweise sieht der so aus:

chrome_colorpicker

chrome_colorpicker

Das Dropdownfeld ist auch keine Überraschung:

dropdown

dropdown

Aber jetzt wirds gleich interessant, ich habe nämlich für die Zerlegung des Hex-Wertes in seine RGB-Komponenten eine schicke Funktion gefunden, das geht mit sscanf():

<?php
require_once('config.php');

if(isset($_POST['senden'])){
    
    
    echo "Sie haben diese Farbe gewählt:<span style='background-color:".$_POST['farbe']."'>Hexadezimalwert: ".$_POST['farbe']."</span><br>";
    
    $akt_farbe = $_POST['farbe'];
    
    //RGB-Werte aus hex holen
    $hex = $akt_farbe;
    list($r, $g, $b) = sscanf($hex, "#%02x%02x%02x");
        
    echo "<h3>Die relative Helligkeit beträgt: ".Brightness($r,$g,$b)."</h3>";
    echo "<h3>Kontrastfarbe aus der Farbfamilie ".$_POST['farbfamilien']."</h3>";
    echo "<hr>";
    $input_brightness = Brightness($r,$g,$b);

Mit der Funktion Brightness wird wie gesagt die wahrgenommene Helligkeit der gewählten Farbe bestimmt, Input-Parameter sind die drei RGB-Komponenten. Die Funktion selber sieht so aus:

//relative (wahrgenommene) Helligkeit aus RGB berechnen
function Brightness($r, $g, $b)
    {
       return sqrt(
          $r * $r * .241 + 
          $g * $g * .691 + 
          $b * $b * .068);
    }

Jetzt hole ich mir aus der Datenbank alle Farben, die zu der gewählten Farbfamilie gehören. Zuerst setze ich noch einen Flag $gefunden, der bleibt auf False wenn in der gewählten Farbfamilie keine Farbe mit einem ausreichenden Kontrastwert gefunden wurde und dient dann weiter unten zur Info-Ausgabe.

Dann gehe ich mit einem While über alle gefunden Farben und gebe nur diejenigen aus, bei denen eine Differenz > 130 zur relativen Helligkeit der vom Benutzer vorgegebenen Farbe gefunden wird.

//alle passenden Einträge der Farbtabelle ausgeben
        //Flag default  
        $gefunden = false;
        
        //Nur die Einträge der gewählten Farbfamilie auslesen
          $abfrage = mysqli_query($conn,"SELECT * FROM $table 
                    WHERE familie LIKE '".$_POST['farbfamilien']."' "); 
          while($row = mysqli_fetch_array($abfrage,MYSQLI_ASSOC)) 
          { 
            //RGB-Werte aus hex holen
            $hex = $row['hex'];
            list($r, $g, $b) = sscanf($hex, "#%02x%02x%02x");
            //Helligkeit bestimmen
            $row_brightness = Brightness($r,$g,$b);
            
            //Kontrastwert berechnen
            $differenz = abs($row_brightness - $input_brightness);
            
            //Nur Kontrast höher als 130 ausgeben (Wert ggf anpassen)
            if ($differenz > 130){
                //flag setzen wenn mindestens ein Kontrast > 130 gefunden wurde
                $gefunden = true;
            echo ' <span style="color:'.$row['farbname'].'">'.$row['farbname'].'</span>.'.$row['hex'].'rel. Helligkeit: '.$row_brightness.' Differenz: '.$differenz.'<br>'; 
            echo "<h1><span style='color:".$row['farbname']."; background-color:".$akt_farbe."'>".$row['farbname']." ist ein guter Kontrast</span></h1>";
            echo "<hr>";
            }
            
        } // ende von while $row...
if($gefunden==false){echo "Keine passende Kontrastfarbe in der Farbfamilie ".$_POST['farbfamilien']." gefunden";}

} //Ende von isset senden

Für die Ausgabe der passenden Kontrastfarben verwende ich Spans und formatiere sie mit den entsprechenden Farbwerten. Falls keine Farbe mit ausreichendem Kontrast gefunden wurde, gebe ich dies als Info aus. Das war’s!

Hier noch ein Screenshot der Ausgabe, als Besipiel habe ich als Grundfarbe Gelb und für die Kontrastfarbe die Farbfamilie Blau gewählt:

farben_ausgabe

farben_ausgabe

Fertig ist das Werkzeug zur Kontrastfarbenbestimmung. Zugegeben könnte man es noch wesentlich hübscher formatieren, aber die Funktionalität haut hin, das genügt mir jetzt erstmal. Schlanke noch nicht mal 100 Zeilen Code, das ist mal wieder was für mich und meine minimalistischen Freunde – viel Spaß beim Nachbauen!

Nachtrag

Jetzt konnte ich mir es doch nicht verkneifen, die ganze Sache noch ein bißchen ansehnlicher zu gestalten. Der Prototyp sieht jetzt so aus:

aufgehuebscht

aufgehuebscht

Die Farben hole ich mir natürlich aus der Datenbank, wenn ich dazukomme, schreib ich noch einen neuen Artikel darüber. Aber hier ist jetzt mal Schlussende, der Beitrag ist lang genug

 


 

 

Barrierefreie Farbkontraste – ich hab da so ne Idee

Also, wie man ausrechnet ob auf einem farbigen Hintergrund eine schwarze oder weiße Schriftfarbe besser lesbar ist, das hatten wir ja gerade eben schon mal. Jetzt ist der Kunde aber König, und man kommt auch mal in die Verlegenheit, eine farbige Schrift auf farbigem Grund darstellen zu sollen. Die Anforderung könnte also heissen: es soll eine grüne Schrift auf rotem Grund ausgegeben werden, und die soll möglichst gut lesbar sein. Da kann man einen Colorpicker nehmen und so ungefähr schauen was besser lesbar ist, aber das ist ja doch bloß Trial&Error. Es gibt auch Formeln, mit denen man so etwas berechnen kann, fündig wird man z.B. beim W3C, oder auf dieser Seite von nbtech. Nach deren Algorithmus kann man die „perceived brightness“, also die wahrgenommene Helligkeit einer Farbe als Wert zwischen 0=schwarz und 255=weiß berechnen. Das ist nicht trivial, weil z.B. grüne Töne vom menschlichen Auge als heller wahrgenommen werden als rote Schattierungen. Darum berechnet man die jeweilige wahrgenommene Helligkeit, und vergleicht die Farben dahingehend. Ein Unterschied von ca. 120 zwischen den zwei Farben wird nach W3C als ausreichender Kontrast angesehen. Die Formel lautet wie folgt:

brightness  =  sqrt( .241 R2 + .691 G2 + .068 B2 )

sqrt ist die Quadratwurzel, R, G, B sind unsere guten alten Bekannten Rot- Grün- und Blauanteil aus dem RGB-Farbraum, wie wir sie in HTML so gerne verwenden (ausführliche Infos hierzu z.B. hier bei W3Schools)

Gar nicht so einfach: was ist Rot, was ist Grün?

Auch das ist nicht trivial, weil von der Meinung des Betrachters abhängig. Ein dunkles Rotbraun mag für einen noch als Rot durchgehen, der nächste ordnet es schon den braunen Tönen zu. Ein sonniges Gelb kann auch als Orange gelten, und ein blaustichiges Grün als Blauton herhalten. Sowas kann man nicht berechnen, da kann man sich nur eine Krücke suchen und daran festhalten. Bei den 16,7 Mio vorhandenen RGB-Farben ist es auch ein Ding der Unmöglichkeit, überall die korrekte Farbfamilie zu bestimmen. Wir nehmen ein paar Farben weniger, das tuts als Arbeitshypothese auch.

Eine Untermenge: die benannten HTML-Farben

Ich habe da eine interessante Zusammenstellung der benannten HTML-Farben im Web gefunden. Bei https://htmlcolorcodes.com/color-names/ gibt es eine Tabelle der von allen modernen Browsern unterstützten benamsten Farben, die nach Farbfamilien (Color Groups) gruppiert ist. Auch bei w3schools gibt es eine gut gestaltete Seite über die Color Groups, nur sind sie da ein bißchen anders zusammengefaßt.

Die Seite von htmlcolorcodes.com habe ich mir mal als Arbeitsgrundlage geklemmt und in eine Datenbanktabelle gejagt. Es sollten eigentlich 140 Farben sein, auf der Seite sind aber 143 Farben aufgeführt. Die Tabelle enthält Duplikate, da werden Farben unter zwei verschiedenen Farbfamilien genannt. Ist jetzt aber nicht so schlimm, letztendlich ist die Information wertvoll, welche Farbtöne als Rot, Blau, Gelb usw. empfunden werden. Es gibt eine Liste von 10 Farbfamilien. Ich werde bei den englischen Bezeichnungen bleiben, schliesslich sind auch die HTML-Farbnamen in Englisch.

Hier mal die Liste der Farbfamilien, mit der jeweiligen Anzahl der zugeordneten Farben:

YELLOW    11
WHITE    17
RED    9
PURPLE    19
PINK    6
ORANGE    6
GREEN    23
GRAY    10
BROWN    17
BLUE    25

Wie gesagt, die Summe ist hier 143, nicht 140, aber das vernachlässigen wir jetzt mal. Dass Blue die meisten Farbtöne zugeordnet hat verwundert auch nicht weiter, da Blau nunmal eine sehr beliebte Farbe bei Webdesignern und Grafikern ist und im Web sehr häufig verwendet wird.

Wer sagt, dass das Rot ist?

Unter der Farbfamilie „Red“ sind 9 benannte Farben zu finden:

family_red

family_red

Da sieht man schon, daß die Zuordnung der einzelnen Farbtöne zu denen Farbfamilien ausgesprochen subjektiv und diskutabel ist, Lightcoral würde ich eher unter Pink einordnen. Wenn man sich die RGB-Werte dieser Farben anschaut, wird man auch nicht schlauer.

red_family_rgb

red_family_rgb

Es ist mitnichten so, dass diese Farben alle einen besonders hohen R-Anteil haben, Darkred ist zum Beispiel so ein Ausreisser mit 139 als R-Wert, dafür sind die G-und B-Werte Null. Ich hab lange rumgesucht, aber bisher keine praktikable Berechnungsmethode gefunden, mit der man x-beliebige Farbtöne einer der obengenannten Farbfamilien zuordnen kann. Deswegen verwende ich die Zuordnungen von htmlcolocodes.com, da hat man zumindest mal einen Anhaltspunkt.

Und wozu das Ganze?

Ganz einfach: mir schwebt sowas wie ein spezialisierter Colorpicker vor, in dem man eine frei wählbare Farbe vorgibt. Dann wählt man aus, aus welcher Farbfamilie die Kontrastfarbe kommen soll, und erhält einen oder mehrere Vorschläge, welche Farbtöne einen guten Kontrast bieten würden. Das als Arbeitshypothese. Mal sehen, wie weit ich damit komme.

Die Voraussetzung: eine MySQL-Tabelle mit allen 140 benannten Farben

Diese enthält die Farbfamilie, den Farbnamen, den Hex-Wert und den RGB-Wert. Die habe ich natürlich nicht per Hand eingehackt, da habe ich einen kleinen Umweg über ein Excel-Makro genommen, mehr wird nicht verraten. Die Tabelle sieht so aus, die Feldnamen sind selbsterklärend:

mysql_farbtabelle

mysql_farbtabelle

Das wird meine Ausgangsbasis. Und ich komme mit unter 100 Zeilen Code aus. Den Source gibt es dann im nächsten Beitrag.