WBCE CMS Forum

WBCE CMS – Way Better Content Editing.

Du bist nicht angemeldet.

#1 09.09.2015 14:16:18

norhei
Developer

Ein Autoloader für WB

Autoloader haben eine riesen Menge Vorteile:

  • Erweiterungen(z.B. Snippets) die nur geladen werden wenn sie benötigt werden.

  • Core Komponenten werden ebenfalls nur noch bei Bedarf geladen.

  • Der Include/Require(Once) Wirrrwar fällt komplett flach.

  • Nichts wird versehentlich mehrfach geladen.

  • Klassenaufrufe werden einfacher(Der Code drumrum auch).

  • ...

Der Autoloader von BC bietet leider keine Möglichkeit Autoloading auch in Modulen zu nutzen. Natürlich können Module auch ihren eigenen Autoloader definieren, aber davon das Module wirklich ALLES selber machen müssen wollen wir ja grade doch ein wenig weg (zumal man da ja auch wissen muss was man tut ).

Ich habe mir jetzt mal die Mühe gemacht den Autoloader aus meinem alten Fork in moderner Version neu zu schreiben. Der Autoloader ist Registry basiert und dürfte damit WB Style sehr nahe kommen.

Direkt bei Aufruf können die Standard Verzeichnisse hinzugefügt werden.

Danach registriert man neue Verzeichnisse mit z.B.:

WbAuto::AddDir("/classes/");

Diese Verzeichnisse werden dann nach zu den Schematas passenden Dateien durchsucht.

Für Spezialfälle (externe Scripte ...) gibt es die Variante direkt Klassen und Dateien zu verknüpfen.

WbAuto::AddFile("MultiCrud","/classes/multicrud.php");

So etwas erspart viel Wartungsarbeit wenn man externe Bibliotheken nutzt/upgraded , die nicht immer sinnvoll benannt sind.

Weitere Schemata kann man mit 

WbAuto::AddType("class.%s.inc.php");

hinzufügen.

Alles ist Global und kann von Überall her aufgerufen werden(sorgenfrei, keine Handler usw.). Auch die  Variablen $Dirs, $Files; Types, können zum Debuggen überall erreicht werden.

echo  "<pre>"; print_r(WbAuto::$Dirs); echo  "</pre>";
echo  "<pre>"; print_r(WbAuto::$Files); echo  "</pre>";
echo  "<pre>"; print_r(WbAuto::$Types); echo  "</pre>";

Hier noch die ganze kleine Klasse. Klein einfach und nützlich.

class WbAuto{

    // Vars
    public static $Dirs=array();    // Dirs to search
    public static $Files=array();   // Class filename combinations for loader 
    
    //Search templates to search for files in the directories
    public static $Types=array('%s.class.php',
                               '%s.class.inc', 
                               '%s.php', 
                               '%s.inc', 
                               '%s.inc.php', 
                               'class.%s.php', 
                               'class.%s.inc'); 
    
    // The function actually registered to PHP autoloading
    static function Loader($ClassName){
        //already loaded, never mind
        //echo $ClassName;
        if (class_exists($ClassName, FALSE)) return FALSE; 
        
        //if there is an fileentry for this class
        if (array_key_exists ($ClassName, WbAuto::$Files)) { 
            //echo (WbAuto::$Files[$ClassName]); 
            require_once (WbAuto::$Files[$ClassName]); 
            return FALSE;
        }
        
        //Search dirs for matching class files
        foreach (WbAuto::$Dirs as $sDirVal) {
            foreach (WbAuto::$Types as $sTypeVal) {
                $sTestFile= $sDirVal. sprintf($sTypeVal, $ClassName);
                $sTestFile= strtolower($sTestFile);
                //echo $sTestFile."</br>";
                if (is_file($sTestFile)) {
                    include_once($sTestFile);
                    return FALSE;
                }
            }
        }   
    }
    
    // add a single class file 
    // WbAuto::AddFile("MultiCrud","/classes/multicrud.php");
    static function AddFile ($ClassName="", $ClassFile="", $overwrite= false){
    
        //Check for valid call return error if invalid
        if ($ClassName=="") return ("No class name set!");
        if ($ClassFile=="") return ("No class file set!");
        if (isset(WbAuto::$Files[$ClassName]) and $overwrite== false) return  ("Class already exists ($ClassName)");
        
        // More Checks
        $LoadFile = WB_PATH.$ClassFile;
        if (!is_file($LoadFile)) return ("Not a file: $LoadFile");
        if (!is_readable($LoadFile)) return ("File not readable: $LoadFile");  
         
        // if all is ok set new Classfile 
        WbAuto::$Files[$ClassName]=$LoadFile;
        return FALSE;
    
    }
 
    // add a full directory to search for matching classfiles
    // WbAuto::AddDir("/classes/fields");
    static function AddDir ($Dir){
 
        // for windooze
        $Dir=str_replace ("\\", "/", $Dir);
        
        //always trim at front end end
        $Dir=trim($Dir, "/");
        
        //construct full dir
        $AddDir=WB_PATH."/".$Dir."/";
        
        //Some checks
        if (!is_dir($AddDir)) return ("Not a directory: $AddDir");
        if (!is_readable($AddDir)) return ("Directory not readable: $AddDir");
        
        // Add Dir
        WbAuto::$Dirs[]=$AddDir;
        
        //avoid double entries
        WbAuto::$Dirs= array_unique(WbAuto::$Dirs);
 
        return FALSE;
    }
 
    // Add a new template to search for in directorys.
    // WbAuto::AddType("class.%s.inc.php");
    static function AddType ($Type){

        // Add Search template
        WbAuto::$Types[]=$Type;
       
        //avoid double entries
        WbAuto::$Types= array_unique(WbAuto::$Types);

        return FALSE;
    }
}

//finally register this autoloader
spl_autoload_register('WbAuto::Loader');

Das Ganze mach ich später noch tauglich für den PHP Dokumentor.

Offline

#2 09.09.2015 19:36:24

webbird
Administrator

Re: Ein Autoloader für WB

Wenn sich Module zur Laufzeit registrieren, hast Du dann nicht ein Henne-Ei-Problem?


Ich habe eine Amazon-Wishlist. wink Oder spende an das Projekt.
Ich kann, wenn ich will, aber wer will, dass ich muss, kann mich mal

Offline

#3 10.09.2015 01:06:52

norhei
Developer

Re: Ein Autoloader für WB

Wenn ich das mit Henne und Ei richtig verstehe würde ich sagen ... Jein.

In meinem Fork hatte ich das so weit getrieben , das alle Module eine init.php hatten wo sie erstens Funktionalität ganz weit am Anfang des Init Prozeses schon einbringen konnten(brauchte ich für ein multidomain modul) und obendrein natürlich auch alle Ihre Klassen registrieren konnten. Ich hatte auch den Mailer und den Wandler für die IDN-Domains damit dann als Modul gebaut. Damit war also das erste was nach dem Laden des Autoloaders registriert wurde die Core Ordner(Wie bei Dir) und dann direkt alle Moduleklassen.

Wenn man den ohne Änderungen im Init Prozess implementiert haben wir Folgenden Zustand.

Snipits .. Funktioniert Prima, Snipits werden lange bevor sie gebraucht werden geladen.

Core .. Da die Core Verzeichnisse ja direkt mit dem Autoloader eingebunden werden , keine Probleme.

Module ... Module werden ja nur aufgerufen, wenn sie benutzt werden und da macht es dann tatsächlich keinen Unterschied ob man nun eine Zeile mit Include die Klassen läd oder mir Register sie registriert. Da bietet es halt keinen Vorteil

Allerdings mit einigen winzigen Änderungen am  Init können Module ja Ihre eigene include.php haben (So was ist jetzt ja nur mit Tricks zu erreichen , siehe Ruuds Slider der aus einem Admintool mit einem dazugetricksten Snipit install besteht. Damit wäre aber auch das Problem Autoload gelöst.

Oder Siehst du noch weitere    Henne-Ei-Probleme?

Offline

#4 10.09.2015 11:07:00

webbird
Administrator

Re: Ein Autoloader für WB

Ich glaube, ich habe den Vorteil noch nicht verstanden, aber macht nichts. big_smile Irgendwie ist eine Registrierung bei einem Autoloader für mich ein Widerspruch in sich. Aber das muß nix heißen, ich verstehe auch andere Konstrukte nicht. wink

Bei BC will ich die Snippets eigentlich komplett abschaffen, es macht keinen Sinn, include-Dateien zu laden, weil das Snippet VIELLEICHT benutzt wird. Ich sagte ja schon, aus meiner Sicht sind Droplets die Nachfolger und Ablösung von Snippets, jedenfalls mit Ralfs DropletsExtensions, die ja in BC Bestandteil des Core sind.

Aber letztlich führen ja auch immer mehrere Wege nach Rom, und meist ist es müßig, über den besten Weg zu streiten. big_smile


Ich habe eine Amazon-Wishlist. wink Oder spende an das Projekt.
Ich kann, wenn ich will, aber wer will, dass ich muss, kann mich mal

Offline

#5 10.09.2015 11:57:37

norhei
Developer

Re: Ein Autoloader für WB

Prinzipiell könnte man auch einfach alle Modulordner durchforsten wenn ein Ordner /classes/ existiert, oder möglicherweise beim Install oder in der Info das ganze gleich hinterlegen. Das wäre dann sozusagen Vollautomatisch. Die Registrierung bräuchte man dann nur noch für Zeugs ausserhalb von WB.

Snippets mache zumindest im Moment schon Sinn aber eben nur für größere Sachen. Templateengine , AnyTopics.... das besteht ja alles auch aus einer ganzen Menge Dateien. Bei dem Gedanke so komplexe Dinge über ein Droplet interface einzufügen .... Brrrr....

Wo finde ich mal mehr Info zu den Droplet Extensions ? Wie siehts da mit Sprachdateien aus?

Wo steht vor allem das nicht beide Wege gut sind oder in bestimmten Situationen Ihre Vorteile haben und vielleicht ist es einfach Sinnvoll beides zu behalten? Nebenbei die Diskussion ist so wunderbar entspannt und sachlich, das es auf jeden Fall ein Gewinn ist.  (hab schon wieder ne Handvoll neuer Ideen big_smile ) Nur dadurch das man Ideen bespricht und auf den Prüfstand stellt, komm was vernünftiges dabei raus. Was passiert wenn man sich nur in sein Kämmerlein zurückzieht, das kennen wir ja zu Genüge von Classic.

Offline

#6 10.09.2015 12:20:40

norhei
Developer

Re: Ein Autoloader für WB

Nachtrag:
Aus Droplets extensions Readme https://github.com/phpManufaktur/DropletsExtension

<?php
  if (file_exists(WB_PATH.'/modules/droplets_extension/interface.php')) {
    require_once(WB_PATH.'/modules/droplets_extension/interface.php');
    print_page_head();
  }
?>

Könnte mit einer in der include.php registrierten Klasse einfach so aussehen:

if (class_exists('DrEx'))   DrEx::PageHead(); 

Das finde ich nun doch irgendwie einfacher ?

Droplets Extensions mache auch einige Dinge die eigentlich der Core direkt machen sollte :

Extend Droplets for the Content Management Systems WebsiteBaker and LEPTON CMS with the ability to integrate into the CMS search function, to load CSS and JS files

Die Möglichkeit von jeder Stelle im CMS auf einfache Weise  JS, CSS, METAS usw. in den Header laden zu können  sollte eigentlich der Core oder ein Core Modul bieten, damit würde es dann auch nicht nur in den Droplets sondern wirklich überall funktionieren. Wir sind im Moment in der wunderbaren Lage, das keiner mehr solche Verbesserungen blockiert.

Beitrag geändert von norhei (10.09.2015 12:21:49)

Offline

#7 10.09.2015 12:21:38

webbird
Administrator

Re: Ein Autoloader für WB

Naja, der klassische Autoloader funktioniert ja so: "Irgendwas" wird nicht gefunden, und der Name von dem "irgendwas" wird dem Autoloader übergeben. Was der nun damit anfängt, ist ja seine Sache. Man kann Autoloader auch verketten, bzw., das passiert automatisch, sofern man es nicht bewußt unterbindet. Insofern kann man tatsächlich zunächst den framework-Ordner durchsuchen und danach die Modulverzeichnisse. Natürlich dauert das etwas länger, als wenn man irgendwo eine Liste von Klassen und deren Verzeichnissen hat.

Man könnte das aber wunderbar kombinieren, indem man einmal gefundene Klassen in die Datenbank oder eine dateibasierte Registry (JSON oder INI oder was auch immer) einträgt. Dann muß der Autoloader beim nächsten Mal nicht mehr die Verzeichnisse abscannen, sondern kann zuerst in seiner Registry nachgucken. Das wäre quasi eine Kombination aus klassischem Autoloader und Registry. Ob das performant ist oder nicht wäre zu testen.

Die Doku zu den DropletsExtensions ist hier wieder online: http://www.phpmanufaktur.info/de/addons … ension.php

Ich glaube, ich hatte das Prinzip schon mal erklärt, aber vielleicht nicht ausführlich genug.

Bei Snippets stört mit in erster Linie, daß der Core alle include.php einbindet, ganz egal, ob ein Snippet überhaupt benutzt wird. Je mehr Snippets man installiert hat, desto mehr potentiell unnötigen Ballast inkludiert der Core. Und je umfangreicher die include.php sind, umso mehr Zeit kostet das.

Ein umfangreiche(re)s Droplet steht nicht für sich allein, sondern "besitzt" sozusagen sein eigenes Modul. In WB gibt es für Module, die weder Snippets noch Page Module sind, kein Modultyp; in BC gibt es dafür den Typ "Library". Das wäre dann quasi der Anytopics-Fall. Oder man nimmt den Snippets-Typ, legt aber keine include.php in das Modul. Ist ja letztlich egal. Das Modul kann dann so komplex sein, wie es will, es kommt erst zum Einsatz, wenn es wirklich gebraucht wird - im Unterschied zu den Snippets. Dabei kann der Droplet-Code selbst total undramatisch sein, im einfachsten Fall wird nur eine Klasse geladen und alles weitere dorthin delegiert. (So hab ich z.B. Piwik eingebunden.) Man muß also nicht etwa alles in das Droplet packen.

Wenn man aber ein komplettes Modul dahinter hat, hat man alles - Sprachdateien, CSS, JavaScript, was auch immer. Die DropletsExtensions hatten dafür eine Methode print_page_head(), die zusätzlich im Template untergebracht wird. Damit kann man dann sogar META-Einträge zurückliefern. (Bei BC gibt es ja ohnehin nur noch den einen Aufruf get_page_headers(), der alles liefert - vom TITLE über CSS und JS bis hin zu beliebigen METAs. Das print_page_head() der DropletsExtensions ist da quasi als Bestandteil mit enthalten.)


Ich habe eine Amazon-Wishlist. wink Oder spende an das Projekt.
Ich kann, wenn ich will, aber wer will, dass ich muss, kann mich mal

Offline

#8 10.09.2015 12:24:19

webbird
Administrator

Re: Ein Autoloader für WB

norhei schrieb:

Die Möglichkeit von jeder Stelle im CMS auf einfache Weise  JS, CSS, METAS usw. in den Header laden zu können  sollte eigentlich der Core oder ein Core Modul bieten, damit würde es dann auch nicht nur in den Droplets sondern wirklich überall funktionieren.

Jepp. So ist es in BC umgesetzt. Das sorgt sogar dafür, daß "verspätet" hinzugefügte CSS-Dateien trotzdem im Header landen. Funktioniert dann zwar nur mit JavaScript, aber hey - irgendeine Einschränkung gibt es immer.

Manchmal frag ich mich wirklich, warum Ihr hier eigentlich BC nachbauen wollt... lol  lol  lol


Ich habe eine Amazon-Wishlist. wink Oder spende an das Projekt.
Ich kann, wenn ich will, aber wer will, dass ich muss, kann mich mal

Offline

#9 10.09.2015 12:44:26

norhei
Developer

Re: Ein Autoloader für WB

Hmmmm, das mit der Registry die die Verzeichnisse durchsucht hab ich schon öfter mal gesehen. Fand ich gar nicht schlecht.  Denke am sinnvollsten wäre es eine solche Registry immer einmal beim installieren und deinstallieren von Modulen/Templates durchlaufen zu lassen und dann in DB oder Datei abzulegen. Wenn der Durchlauf beim Install/Deinstall stattfindet ist Performance definitiv kein Problem.

Bei Snippets stört mit in erster Linie, daß der Core alle include.php einbindet, ganz egal, ob ein Snippet überhaupt benutzt wird. Je mehr Snippets man installiert hat, desto mehr potentiell unnötigen Ballast inkludiert der Core. Und je umfangreicher die include.php sind, umso mehr Zeit kostet das.

Genau darauf hatte ich Bezug genommen, vielleicht nicht deutlich genug.

Einfach in der include.php:

<?php WbAuto::AddDir("/mysnippit/classes/");

Und schon hast Du ein Library Modul  tongue  und der Ballast fällt weg. Wenn die Klassen dann auch noch Standard konform benannt sind , findet er sie sofort. Ansonsten probiert er halt noch ein paar Kombinationen. Aber, die alten Snippits funktionieren trotzdem und wer kein OOP möchte kann weitermachen wie bisher.

Wobei ich den Gedanken separate Lib Module zu machen auch recht gut finde.


Damit kann man dann sogar META-Einträge zurückliefern. (Bei BC gibt es ja ohnehin nur noch den einen Aufruf get_page_headers(), der alles liefert - vom TITLE über CSS und JS bis hin zu beliebigen METAs. Das print_page_head() der DropletsExtensions ist da quasi als Bestandteil mit enthalten.)

Genau das sollte eigentlich hier der Core auch mitliefern... und nicht irgendein Modul. Deswegen müssen wir auch nochmal mit der Datenklasse schauen  smile

Offline

#10 10.09.2015 12:46:49

webbird
Administrator

Re: Ein Autoloader für WB

norhei schrieb:

Damit kann man dann sogar META-Einträge zurückliefern. (Bei BC gibt es ja ohnehin nur noch den einen Aufruf get_page_headers(), der alles liefert - vom TITLE über CSS und JS bis hin zu beliebigen METAs. Das print_page_head() der DropletsExtensions ist da quasi als Bestandteil mit enthalten.)

Genau das sollte eigentlich hier der Core auch mitliefern... und nicht irgendein Modul. Deswegen müssen wir auch nochmal mit der Datenklasse schauen  smile

Genau! Wobei wir in BC dafür keine Registryklasse benötigen. Aber wie gesagt, es geht ja nicht drum, BC nachzubauen.


Ich habe eine Amazon-Wishlist. wink Oder spende an das Projekt.
Ich kann, wenn ich will, aber wer will, dass ich muss, kann mich mal

Offline

#11 10.09.2015 12:56:55

norhei
Developer

Re: Ein Autoloader für WB

Manchmal frag ich mich wirklich, warum Ihr hier eigentlich BC nachbauen wollt...

 

Manche Lösungen sind einfach Logisch vorgegeben, da kann man eventuell über das "Wie" reden, aber sie sind einfach notwendig. Wenn das damals mit der mehrfachen Abspaltung so friedfertig abgegangen wäre wie jetzt bei Florian, dann würden wir möglicherweise jetzt alle an BC arbeiten. Vielleicht war die Zeit damals auch noch nicht reif weil zu viele noch am DVs Sprüche geglaubt haben.  Vielleicht werden die Projekte irgendwann in der Zukunft auch gemerged... Wäre auch nicht schlimm. Wobei der Fokus hier mehr auf WB Reloaded liegt und Du in deinem Fork weitaus hemmungsloser alte Zöpfe abgeschnitten hast.

Auf jeden Fall macht das Mitarbeiten/Zusammmenarbeiten  hier richtig Spaß. Kein Stress, keine Aufregung, fein Frust , alle benehmen sich.... einfach schön.

Offline

#12 10.09.2015 12:58:30

norhei
Developer

Re: Ein Autoloader für WB

Wobei wir in BC dafür keine Registryklasse benötigen

Klar geht auch ohne, wo speichert BC die Daten zwischen?
So eine Klasse kann halt gleich Doubletten entfernen und so ...

Beitrag geändert von norhei (10.09.2015 12:59:40)

Offline

#13 10.09.2015 13:53:50

webbird
Administrator

Re: Ein Autoloader für WB

BC braucht nichts zwischenspeichern. Die Module und Templates haben eine headers.inc.php, in der sie konfigurieren können, was sie brauchen. Und die "alten" oder "normalen" Dinger wie frontend.css werden nach wie vor automatisch gesucht. Das geht alles on-the-fly.

Zur friedfertigen Abspaltung: 1. bin ich nicht sicher, ob DV das überhaupt mitgekriegt hat. Und 2. ist ja sonst keiner mehr übrig, der sich aufregen könnte. Das war damals noch anders.


Ich habe eine Amazon-Wishlist. wink Oder spende an das Projekt.
Ich kann, wenn ich will, aber wer will, dass ich muss, kann mich mal

Offline

#14 10.09.2015 16:53:31

norhei
Developer

Re: Ein Autoloader für WB

Also ist die headers.inc.php sowas wie die Frontend.js  nur eben mit PHP  ?

Meine Vorstellung ging eher in Richtung einer Klasse die von überall heraus (Core, Modul, Snipit, egal) Scripte, Metas und was auch immer für den Header registriert und dann letztlich über Droplets ganz am Schluss einfügt. Damit umgeht man auch das Henne Ei Problem , das nämlich print_page_header() , frontend functions oder simple_pagehead  aufgerufen werden bevor die meisten anderen Snipits/Module gelaufen sind. Die werden ja in der Reihe der Aufrufe im Template aufgerufen. Droplets kommen ja erst zum Schluss und deswegen können alle Module, Snipits usw. ihre Scripte registrieren und erst ganz kurz vor Schluss wird der Header fertiggestellt.

Also mann könnte dann überall im Code einfach so Übergeben:

// Fancybox im <head> laden.
WbInject::JsFileHead("fancybox", "/pfad/zu/meinem/fancybox.js") 

// Direktes script Schnipsel im Footer einfügen(eigentlich für Piwik und co.)
WbInject::JsScriptFoot ("alert", 'alert("hello")')  
    
// CSS wird grundsätzlich nur im Head eingefügt                       
WbInject::CssFile("fancybox", "/pfad/zu/meinem/fancybox.css")    

Und im Template würde das dann so eingefügt:

<head>
    [[title]]

    [[meta]]

    [[css]]

    [[jshead]]
</head>
<body>

    Some Content
    Some Content
    Some Content
    Some Content
    Some Content


    [[jsfoot]]
</body>

Aber das ist alles noch ein Wenig Zukunftsmusik

Beitrag geändert von norhei (10.09.2015 17:14:29)

Offline

#15 10.09.2015 17:03:56

webbird
Administrator

Re: Ein Autoloader für WB

norhei schrieb:

Also ist die headers.inc.php sowas wie die Frontend.js  nur eben mit PHP  ?

Nee. Ganz so einfach ist das nicht. Aber ich glaube, das gehört hier in dieser Detailtiefe nicht hin.


Ich habe eine Amazon-Wishlist. wink Oder spende an das Projekt.
Ich kann, wenn ich will, aber wer will, dass ich muss, kann mich mal

Offline

#16 10.09.2015 17:15:50

norhei
Developer

Re: Ein Autoloader für WB

Meinte auch nur vom Basiskonzept.

Offline

#17 10.09.2015 17:31:28

webbird
Administrator

Re: Ein Autoloader für WB

Das Basiskonzept ist ganz einfach: Wer sagt denn, daß page_content() die erste Funktion sein muß, die ermittelt, welche Module auf einer Seite eingebunden sind? cool


Ich habe eine Amazon-Wishlist. wink Oder spende an das Projekt.
Ich kann, wenn ich will, aber wer will, dass ich muss, kann mich mal

Offline

#18 10.09.2015 19:11:35

norhei
Developer

Re: Ein Autoloader für WB

Auch eine nette Variante die Barriere zu überwinden.

Offline

#19 10.09.2015 19:12:37

webbird
Administrator

Re: Ein Autoloader für WB

Vor allem, wenn man diese Information cache-t, damit man das nicht zweimal machen muß. big_smile


Ich habe eine Amazon-Wishlist. wink Oder spende an das Projekt.
Ich kann, wenn ich will, aber wer will, dass ich muss, kann mich mal

Offline

#20 10.09.2015 21:08:04

norhei
Developer

Re: Ein Autoloader für WB

Das schöne ist das man bei meiner wie bei Deiner Variante(vermute ich jetzt mal) das ganze auch nett durch ein Minify drehen kann oder durch nen Less Compiler.

Offline

#21 11.09.2015 10:18:02

webbird
Administrator

Re: Ein Autoloader für WB

Richtig. Ist allerdings nicht gerade trivial. Ich habe das Thema bei BC gerade auf eine spätere Version verschoben.


Ich habe eine Amazon-Wishlist. wink Oder spende an das Projekt.
Ich kann, wenn ich will, aber wer will, dass ich muss, kann mich mal

Offline

Fußzeile des Forums

up