Ringgleis-Rallye

Letztes Jahr hatte ich eine Fahrrad-Rallye zum Braunschweiger Ringgleis veranstaltet. Da ich der Meinung bin, dass sich solch eine Veranstaltung geradezu anbietet, per Software unterstützt zu werden, schrieb ich die R-Rallye:

Die App ist eine Browser-Anwendung, voll funktionsfähig und für mobile Nutzung optimiert. Ein großer Teil der Aufgaben ist jedoch leider inzwischen veraltet, da sich die lokalen Gegebenheiten (z.B. Graffitis) geändert haben. Wenn ich mal wieder Zeit habe, werde ich die Aufgaben aktualisieren. Dabei unterstützt mich der Task-Editor. (Bei aktualisierten Aufgaben und Antworten wird der Task-Editor von hier natürlich nicht mehr erreichbar sein)

Exkurs über Webhosting

Hier eine kleine Übersicht über die Funktionsweise und die Bestandteile des ‚Webhostings‘. Also: wie veröffentliche ich eine eigene Website? Und zwar ohne mich bei Webbaukasten-Anbietern registrieren zu müssen und damit in den meisten Fällen im Netz eines Anbieters gefangen zu sein. Guckstu:

Wenn du eine Website erstellen willst, brauchst du einen Webhoster. Bei diesem bestellst du ein (kostenpflichtiges) Hostingpaket mit Wunschdomain („irgendeinentollenseitennamen.de“) und bekommst damit als Eigentümer Zugang zur Domain-Verwaltung und zum Webspace. Mithilfe eines FTP-Programms (z.B. FileZilla) und den vom Webhoster mitgeteilten Zugangsdaten machst du den Datei-Upload in deinen Webspace. Wenn du WordPress verwenden willst, lädst du dir die zip-Datei von wordpress.org herunter und packst den Inhalt des Unterordners ‚wordpress‘ auf den Webspace. Mittels der Domain-Verwaltung sagst du dem System, welcher Ordner des Webspace der Domain zugeordnet werden soll. Für WordPress (und andere CMS) benötigst du eine Datenbank, die über die Domain-Verwaltung eingerichtet werden kann. Nun kann die Designverwaltung aufgerufen werden (bei WordPress initial aufzurufen mit der domain und im Folgenden mit domain/wp-login) und der Spaß beginnt.

Die vorangegangenen Schritte sind im Allgemeinen nur einmalig auszuführen. Alles Folgende passiert innerhalb der Designverwaltung (z.B. innerhalb von WordPress).

Zu WordPress:

Worpress ist historisch gesehen eine Software , um Web-Blogs zu erstellen. Aus diesem Grunde unterscheidet die Admin-Oberfläche zwischen ‚Beiträgen‘ und ‚Seiten‘. Standardmäßig gibt es immer eine Blogseite, auf der die Beiträge gesammelt werden, und alle anderen Seiten, die üblicherweise auch im Menü zu finden sind. Ausgangspunkt für das Layout sind die ‚Themes‘. Einige sind bereits in der Installationsversion mitgeliefert, andere lassen sich (oftmals kostenpflichtig) nachinstallieren. Die Themes haben spezifische Einstellungsmöglichkeiten, die über den ‚Design -> Customizer‘ aufzurufen sind. Du solltest dir auf jeden Fall Zeit nehmen, um ein wenig rumzuexperimentieren. Das englischsprachige Manual https://codex.wordpress.org hilft auf jeden Fall.

Hier noch ein Hinweis zu den Sternchen * in der Grafik: alle Zugangsdaten sollten in deinem Passwortcontainer hinterlegt werden, z.B. KeePass.

neue Web-Entwürfe 2020

Heldt-Parkett

Für Werner mache ich einen Relaunch, da es doch zeitgemäßer ist, eine Website auch auf dem Smartphone betrachten zu können :lol (ist live):

Sapori Toscani

Unser Lieblingslebensmittelgeschäft ist Sapori Toscani. Der Inhaberin Justyna mache ich grade einen Vorschlag. Mal sehen, ob sie drauf eingeht.

Homepage für Werner

Für Hutträger Werner habe ich eine neue Homepage erstellt. Ich glaube, das wird die letzte handgemachte Seite von mir sein, denn die ist leider nicht responsiv, also nicht handygeeignet. Es hat aber viel Spaß gemacht, das Hutthema zu verarbeiten.

GaLaBau mit one-file-cms

bitho_IMG_0622

Die Familienmitglieder müssen zum Testen herhalten:
Für meine Schwester Birgit und ihren Mann Thomas, beide sehr erfahrene und ideenreiche Garten- und Landschaftsbauer, hab ich einen Entwurf mithilfe meines one-file-cms gemacht.
Besonders stolz bin ich auf die SEO-kompatible Motor-Heckenschere  !

gd-lizenz

Minimalistisches CMS

An den großen Contentmanagement-Systemen hat mich immer der enorme Umfang genervt. Da werden Unmengen an MBs auf den Webserver geladen und diejenigen, die im Alltag die Inhaltspflege übernehmen sollen, müssen aufwendig angelernt werden. Mal ehrlich: es ist fast einfacher, HTML, CSS und JavaScript von der Pike auf zu erlernen als die Redaktionsoberfäche von Typo3  (oder WordPress 😉 ) zu bedienen. Das hat mich dazu animiert, das onefile_cms zu schreiben. Eine einzige kleine PHP-Script-Datei und ein HTML-Template mit nur 5 Variablen sind die Zutaten. Natürlich kann damit kein Enterprise-Portal entwickelt werden, aber für eine mehrseitige kleine Homepage mit 1-stufigem Menü reicht’s.

<?php

// onefile_cms by G. Drinkmann Ver 0.12 (29.05.2013)
// Contentmanagement-System mit Wiki-Dialekt zur Contentbearbeitung
// Dieser Quelltext kann völlig frei verwendet werden

error_reporting(E_ALL);

// ############################ HTML-Template ##########################################

// das Template sollte folgendes enthalten:

// Variablen im Template:
// <!-- {sitetitle} -->
//    wird ersetzt durch Homepage-Titel
// <!-- {menutitle} -->
//    der Menüname der angezeigten Seite
// <!-- {ul-menu} -->
//    Menü als automatisch erzeugte ul-Liste (CSS s.u.)
// <!-- {content-width} -->
//    wird ersetzt durch Content-Breite
// <!-- {content} -->
//    der eigentliche Seiteninhalt aus der Redaktionsseite

// CSS:
// #menu   id des ul-Elements
// #menu li   z.B.{display:inline; list-style:none;}
// #menu a.menunormal   class-Attribut des a-Elements im Menü
// #menu a.menucurrent   class-Attribut des aktiven a-Elements im Menü
// img.Lfloat   class-Attribut von linksbündig formatierten Bildern mit Text-Umlauf
// img.Rfloat   class-Attribut von rechtsbündig formatierten Bildern mit Text-Umlauf

// Konvention:
// Namen der Bilder des Templates sollten mit "ONEFILECMS_" beginnen

// ------- HTML-Template-Datei ---------------------
$ggtplfile = "tpl1.html";

// ############################ Script-Variablen #######################################

// ------- Homepage-Titel --------------------------------------------------------------
$ggsitetitel = "Kiosk JOJO";

// ------- md5-Passwort (zu erstellen bei z.B. http://www.functions-online.com/md5.html)
$ggpass = "e6b3cd51a7afb493493d0c4a0bb0c8b2"; // jo33jo (bitte comment löschen)

// ------- Edit-URL-Parameter (m=) zum Aufruf der Redaktionsseite ----------------------
$ggeditpara = "edit"; // ohne Leer- oder Sonderzeichen !

// ------- Template ist XHTML ----------------------------------------------------------
$ggisxhtml = false;

// ------- Content-Breite --------------------------------------------------------------
$ggcontwidth = "750px";

// ############################ ab hier Chefsache !! ###################################
$ggcontfile = "content1file.txt";
$ggxsl = ($ggisxhtml) ? " /" : "";
function ggerror($ggmsg) {
    global $ggcontent, $ggsitetitel, $ggxsl;
    $ggcontent[0]['mtitle'] = "FEHLER";
    $ggcontent[0]['read'] = "<i>".$ggmsg."</i><br".$ggxsl."><br".$ggxsl.">Die Seite \"".$ggsitetitel."\" ist momentan nicht verf&uuml;gbar.<br".$ggxsl.">Versuchen Sie es bitte sp&auml;ter nocheinmal.<br".$ggxsl.">Danke !";
}
if (isset($_GET['m'])) { // Check des GET-Parameter
    if ($_GET['m'] === $ggeditpara) $ggpar = "e";
    else $ggpar = intval($_GET['m']);
} else $ggpar = 0;
if ($ggpar === "e") { // Seite editieren
    if (md5($_POST['pass']) === $ggpass && isset($_POST['content'])) {
        file_put_contents("./".$ggcontfile, urldecode($_POST['content']));
        if (isset($_FILES['pic'])) {
            if (preg_match("/\.(jpg|gif|png)$/i", $_FILES['pic']['name']) || ($ggtplfile === $_FILES['pic']['name'])) {
                move_uploaded_file($_FILES['pic']['tmp_name'], "./".$_FILES['pic']['name']);
            }
        }
        if (isset($_POST['delimg'])) {
            for ($i=0; $i<count($_POST['delimg']); $i++) {
                @unlink("./".urldecode($_POST['delimg'][$i]));
            }
        }
    }
    if (file_exists("./".$ggcontfile)) {
        $ggfstr = file_get_contents("./".$ggcontfile);
    } else $ggfstr = utf8_encode("; Bitte mit Inhalt füllen");
    $ggpiclis = ""; // vorhandene Bilder listen
    if ($gghandle = opendir('.')) {
        while (false !== ($ggfile = readdir($gghandle))) {
            if (preg_match("/\.(jpg|gif|png)$/i", $ggfile) && (!preg_match("/^ONEFILECMS_/", $ggfile))) {
                $ggpiclis .= "{{".$ggfile."}}<input type='checkbox' name='delimg[]' value='".$ggfile."' title='Bild l&ouml;schen'><img onmouseover='document.getElementById(\"preview\").src = this.src; document.getElementById(\"preview\").className=\"gvisible\"' onmouseout='document.getElementById(\"preview\").className=\"ghidden\"' class='pici' src='".$ggfile."'> &nbsp;&nbsp;";
            }
        }
        closedir($gghandle);
    }
    if (!$ggpiclis) $ggpiclis = "<i>Keine</i>";
    $ggaction = $_SERVER['PHP_SELF']."?m=".$ggeditpara;
    $ggeditsite = <<<EOTE
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>{$ggsitetitel} editieren</title>
<style type="text/css">
* {font-family:arial}
img.pici {border:0;padding:0;margin:0;height:18px}
img.gvisible {visibility:visible}
img.ghidden {visibility:hidden}
</style>
</head>
<body style="background-color:#ebebeb">
<h2>Website <a href="{$_SERVER['PHP_SELF']}" style="color:red" target="_blank"><i>{$ggsitetitel}</i></a> editieren (Inhalts-Breite <span style="color:red">{$ggcontwidth}</span>):</h2>
<p style="font-size:10px">Zeile mit ; am Anfang = Kommentar<br>
M--- am Anfang der Zeile = neuer Men&uuml;titel<br>
HTML-Tags werden ausgef&uuml;hrt<br>
== am Anfang der Zeile = gro&szlig;e &Uuml;berschrift<br>
=== am Anfang der Zeile = kleinere &Uuml;berschrift<br>
| am Anfang der Zeile = Tabelle (weitere Zellen mit | trennen)<br>
* am Anfang der Zeile = Liste<br>
{{*.jpg oder gif oder png}} = Bild in Originalgr&ouml;&szlig;e<br>
{{*.jpg oder gif oder png|L oder R}} = Bild links bzw. rechts mit Text-Umlauf<br>
[[Men&uuml;index|Linktext oder -bild]] = interner Link (Index beginnt bei 0)<br>
[[Linkadresse|Linktext oder -bild]] = externer Link<br>
**W&ouml;rter** = Fett<br>
//W&ouml;rter// = Kursiv<br>
---- = horizontale Linie<br>
((x)) am Anfang = Text-Umlauf (Float) beenden</p>
<form name="f1" action="{$ggaction}" method="post" enctype="multipart/form-data">
<p><b>Inhalt</b><br>
<textarea name="content" style="width: 600px; height: 300px; font-size: 12pt">{$ggfstr}</textarea></p>
<p id="piclist"><b>Hochgeladene Bilder:</b><br>
{$ggpiclis}</p>
<p><b>Datei hochladen:</b> jpg, gif, png oder Template <i>{$ggtplfile}</i><br>
<input type="file" name="pic" size="61"></p>
<p>Passwort <input type="password" onkeydown="document.getElementsByName('OK')[0].disabled=false" name="pass" style="width: 200px;">
<input style="font-weight:bold" type="submit" name="OK" value="Seite speichern" disabled="disabled"></p>
</form>
<img class="ghidden" id="preview" src="nixda" style="position:absolute; left:3px; top:3px; border:solid #808080 30px;">
</body>
</html>
EOTE;
    echo $ggeditsite;
} else { // normaler Seitenaufruf
    if (file_exists("./".$ggtplfile)) {
        $ggsitestr = file_get_contents("./".$ggtplfile);
    } else die("TEMPLATE NICHT VORHANDEN !");
    $ggcontent = array(); // Initialisierung des Content-Array
    $ggcount=-1;
    if (file_exists("./".$ggcontfile)) { // Steuerdatei ins Content-Array einlesen
        $ggf = fopen("./".$ggcontfile, 'rb');
        while (!feof($ggf)) {
            $ggtmp = rtrim(fgets($ggf));
            if (substr(ltrim($ggtmp),0,4)==="M---") {
                $ggcount++;
                $ggcontent[$ggcount]['mtitle'] = ltrim(substr(ltrim($ggtmp),4));
                $ggcontent[$ggcount]['mtitleclass'] = "menunormal";
                $ggcontent[$ggcount]['read'] = "";
            } elseif (substr(ltrim($ggtmp),0,1)!==";") {
                $ggcontent[$ggcount]['read'] .= $ggtmp."<brbr>";
            }
        }
        fclose($ggf);
        if ($ggcount === -1) ggerror("Fehler: Kein Men&uuml; in Steuer-Datei eingetragen");
    } else ggerror("Fehler: Steuer-Datei nicht vorhanden");

    if ($ggpar>$ggcount) $ggpar = 0;
    $ggcontent[$ggpar]['mtitleclass'] = "menucurrent";
    $ggsitestr = str_replace("<!-- {sitetitle} -->", $ggsitetitel, $ggsitestr);
    $ggsitestr = str_replace("<!-- {menutitle} -->", $ggcontent[$ggpar]['mtitle'], $ggsitestr);
    $ggsitestr = str_replace("<!-- {content-width} -->", $ggcontwidth, $ggsitestr);
    // --- Menü zusammenbauen ---
    $ggmenustr = "<ul id=\"menu\">\n"; 
    for ($i=0; $i<count($ggcontent); $i++) {
        $ggmenustr .= "<li><a href=\"".$_SERVER['PHP_SELF']."?m=".$i."\" class=\"".$ggcontent[$i]['mtitleclass']."\" target=\"_self\">".$ggcontent[$i]['mtitle']."</a></li>\n";
    }
    $ggmenustr .= "</ul>\n";
    $ggsitestr = str_replace("<!-- {ul-menu} -->", $ggmenustr, $ggsitestr);
    // --- Content zusammenbauen ---
    $ggcont = $ggcontent[$ggpar]['read'];
    $ggcont = explode("<brbr>",$ggcont);
    $ggcontstr = "";
    $istab = 0; // Tabelle
    $isul = false; // Bullet-Liste
    $ggcleardiv = "<div style='clear:both;visibility:hidden;width:0;height:0;margin:0;padding:0;border:0;'></div>\n";
    for ($i=0; $i<count($ggcont); $i++) {
        if (substr(ltrim($ggcont[$i]),0,5)==="((x))") {
            $ggisclear = " style='clear:both'";
            $ggcont[$i] = substr(ltrim($ggcont[$i]),5);
        } else $ggisclear = "";
        if (substr(ltrim($ggcont[$i]),0,1)==="|") {
            $ggtra = explode("|",substr(ltrim($ggcont[$i]),1));
            if (!$istab) {
                $ggcontstr .= "<table".$ggisclear.">\n";
            } elseif ($istab !== count($ggtra)) {
                $ggcontstr .= "</table>\n<table".$ggisclear.">\n";
            }
            $istab = count($ggtra);
            $ggcontstr .= "<tr>";
            for ($j=0; $j<$istab; $j++) {
                $ggcontstr .= "<td>".trim($ggtra[$j])."</td>";
            }
            $ggcontstr .= "</tr>";
        } else {
            if ($istab) $ggcontstr .= "</table>\n";
            $istab = 0;
            if ((substr(ltrim($ggcont[$i]),0,1)==="*") && (substr(ltrim($ggcont[$i]),1,1)!=="*")) {
                if (!$isul) $ggcontstr .= "<ul".$ggisclear.">\n";
                $isul = true;
                $ggcontstr .= "<li>".trim(substr(ltrim($ggcont[$i]),1))."</li>";
            } else {
                if ($isul) $ggcontstr .= "</ul>\n";
                $isul = false;
                if (substr(ltrim($ggcont[$i]),0,3)==="===") {
                    $ggcontstr .= "<h2".$ggisclear.">".substr(ltrim($ggcont[$i]),3)."</h2>";
                } elseif (substr(ltrim($ggcont[$i]),0,2)==="==") {
                    $ggcontstr .= "<h1".$ggisclear.">".substr(ltrim($ggcont[$i]),2)."</h1>";
                } elseif (trim($ggcont[$i])==="----") {
                    $ggcontstr .= "<hr".$ggisclear.">";
                } elseif ($ggisclear) {
                    $ggcontstr .= $ggcleardiv.$ggcont[$i]."<br".$ggxsl.">";
                } else $ggcontstr .= $ggcont[$i]."<br".$ggxsl.">";
            }
        }
        $ggcontstr .= "\n";
    }
    $ggcontstr = preg_replace("/{{(\S.*\.(jpg|JPG|gif|GIF|png|PNG))\|L}}/", "<img src=\"$1\" style=\"float:left\" class=\"Lfloat\"".$ggx.">", $ggcontstr); // Bilder mit float links
    $ggcontstr = preg_replace("/{{(\S.*\.(jpg|JPG|gif|GIF|png|PNG))\|R}}/", "<img src=\"$1\" style=\"float:right\" class=\"Rfloat\"".$ggx.">", $ggcontstr); // Bilder mit float rechts
    $ggcontstr = preg_replace("/{{(\S.*\.(jpg|gif|png))}}/i", "<img src=\"$1\"".$ggx.">", $ggcontstr); // Bilder
    $ggcontstr = preg_replace("/\[\[(\d+)\|(\S.*\S|\S)]]/", "<a href=\"".$_SERVER['PHP_SELF']."?m=$1\" target=\"_self\">$2</a>", $ggcontstr); // interne Links
    $ggcontstr = preg_replace("/\[\[(\S+)\|(\S.*\S|\S)]]/", "<a href=\"$1\" target=\"_blank\" title=\"Link in neuem Fenster\">$2</a>", $ggcontstr); // externe Links
    $ggcontstr = preg_replace("/ /", "&nbsp;", $ggcontstr); // Leerzeichen
    $ggcontstr = preg_replace("/&nbsp;(\S)/", " $1", $ggcontstr);
    $ggcontstr = preg_replace("/\*\*(\S.*\S|\S)\*\*/", "<strong>$1</strong>", $ggcontstr); // Fett
    $ggcontstr = preg_replace("#//(\S.*\S|\S)//#", "<em>$1</em>", $ggcontstr); // Kursiv
    $ggsitestr = str_replace("<!-- {content} -->", $ggcontstr, $ggsitestr);
    $ggsitestr .= $ggcleardiv;
    echo $ggsitestr;
}
?>

Also, man nehme den Quelltext, erstelle daraus eine Datei names index.php, fülle die Variablen aus, erstelle eine Template-Datei (mit dem Namen wie bei den Variablen angegeben) und lade beide Dateien (inklusive der zum Template gehörigen Bilder) auf den Webserver. Die index.php-Datei beinhaltet beides: die Maschinerie der eigentlichen Website für den Betrachter und die der Redaktion. Die Redaktionsseite wird über den Link <DeineURL>/index.php?m=<Variable URL-Parameter> aufgerufen. Ok, es sind mehr als nur eine Datei im Spiel, doch das eigentliche Programm besteht in der Tat nur aus dieser einen PHP-Datei.

Das Template sollte eine ganz normale HTML-Seite sein. Die Namen der Templatebilder sollten mit „ONEFILECMS_“ beginnen. Folgende Variablen können in diesem Template plaziert sein:

  • <!– {sitetitle} –> der Homepagetitel
  • <!– {menutitle} –> der Menüname der angezeigten Seite
  • <!– {ul-menu} –> Seiten-Menü als automatisch erzeugte ul-Liste
  • <!– {content-width} –> Breite des Inhaltbereichs
  • <!– {content} –> der eigentliche Seiteninhalt

Hier ist eine kleine Testseite zur Anschauung: der Kiosk des (fiktiven) Jojo

kioskjojo

Das zugehörige Template ist die Datei tpl1.html. Im Quelltext dieser Seite sind die Variablen sichtbar.

Jojo’s Kiosk kann mit der Edit-Seite bearbeitet werden.

kioskjojoedit

Dabei wird zur leicht erlernbaren Bearbeitung ein Wiki-Dialekt angewendet. Hat Ähnlichkeit mit Creole. Zum Speichern wird JEDESMAL das Paßwort abgefragt. Das ist ein wenig lästig, aber sicher.

Flexagon

Ich weiß nicht mehr, wie ich auf Jill Brittons Homepage (leider inzwischen inaktiv) gestoßen bin, aber ich weiß, dass sie in meiner Lesezeichen-Sidebar sehr weit oben steht, weil es dort viele interessante Dinge zu stöbern gibt. Dort gibt es ein Kapitel über Flexagone (unter R-U-B-B-E-R Topic4).

IMG_2867k
ein stark benutztes Flexagon

Ein Flexagon ist ein faszinierendes Gebilde: annähernd 2-dimenional wie ein Blatt Papier (ist ja auch aus Papier gebaut, s. Bild oben), aber du kannst es in einer bestimmten Art entfalten, bzw. auffalten, so dass eine andere verborgene Oberfläche sichtbar wird. Das hier vorgestellte Flexagon heißt Tetra-Tetra-Flexagon und kann als nettes Giveaway dienen. Im fertigen Zustand hat es eine Vorderseite (1.Bild) und eine Rückseite (2.Bild) . Dann kann es zweimal so umgefaltet werden, dass nacheinander zwei zusätzliche Bilder erscheinen (also insgesamt 4 Bilder). Ich habe Jills Fexifier ein wenig getuned (hope you’ll forgive me, Jill):

flexifier

WebGL und mein altes Vergrößerungsgerät

vergroe

Während meiner Tischlerausbildung baute ich mir aus Holz ein Foto-Vergrößerungsgerät.

Als ich vor einigen Wochen wieder im Keller etwas suchte, fand ich meinen alten Schatz wieder.

Das bestätigt mir wieder: wenn ich was suche, finde ich auch etwas (das allerdings nichts mit dem Zielobjekt der Suche zu tun haben muß).

Ich habe die Maße genommen und habe das Gerät in Blender nocheinmal gebaut:

Weil ich ein Fan von 3D auf 2D-Displays (insbesondere im Browser) bin, habe ich anschließend X3DOM getestet. Und hier das Ergebnis