Ausgangszustand
Die Software, Hardware, und Betriebssysteme waren sehr alt und großteils kaum gewartet. Abgesehen davon, dass das meiste auch vom Hersteller nicht mehr unterstützt wurde:
Ein Windows XP Rechner
Library for Windows
Zugang per RDP von den verschiedenen Bibliotheksräumen (Schüler- und Lehrerbibliothek)
Abrechnung und Etikettierung manuell
Ausleihen wurden nur vom Bibliothekspersonal getätigt
Kartensystem als Backup
Neuer Zustand
Ein virtueller Server mit Ubuntu und Koha ist aus dem Schulnetz per http zugreifbar. Administrationsaufgaben werden mit gesonderten Logins erledigt.
Virtueller Server (Ubuntu 19.04)
Koha (Version 19.05.02)
Zugang per http (Schulnetz intern)
Abrechnung immer noch manuell, da nur in Ausnahmefällen wirklich abgerechnet wird
Etikettierung entwickelt sich noch
Ausleihen werden vom Bibliothekspersonal getätigt, um mehr Kontrolle über den Zustand der Bücher zu haben
Kartensystem als Backup
Nutzer können jetzt selbstständig die Bibliothek durchsuchen
Die Vorbereitungen
Zunächst wurden die vorhandenen Daten genau angeschaut. Da "Library for Windows" schlecht bis gar nicht dokumentiert ist, war ein Großteil der Arbeit ein reverseengeneering der Datenbanken. Glücklicherweise verwendet die Software ein Standard Datenbankenformat, sodass das Format nicht auch noch unklar ist, was meinen Horizont dann überstiegen hätte.
Ich musste mir die Tabellen ansehen, mir notieren an welchen Stellen für mich wichtige Daten gespeichert sind und welche Daten ich nicht benötige. Die DBF-Datenbank besteht aus vielen einzelnen Dateien. Jede Datei entspricht einer Tabelle.
Die DBF
-Dateien lassen sich sehr gut mit dem Datenbankbetrachter von Libreoffice Libreoffice Base betrachten unter Fedora installiert man diesen mit dnf install libreoffice-base
. Er zeigt die Dateien so gut und vollständig wie kein anderes Programm, das ich benutzt habe. Ich konnte jedoch keine gute exportfunktion finden, sodass ich diesen Editor immer verwendet habe, um die anderen exporter zu kontrollieren.
Unglücklicherweise wurden viele Tabellen in mehreren Versionen gespeichert, sodass manchmal nicht ganz klar war, welche Version jetzt die aktuelle und vollständige ist, oder dass oft die Daten aus einer Tabelle nur sinnvoll sind, wenn man sie mit Daten aus einer anderen Tabelle kombiniert. Oder die Daten konnten an einer oder einer anderen Stelle gespeichert sein. Hier erstmal ein Überblick über das Datenbanklayout:
CREATE TABLE `DBF_database_library_DAAPROT_dbf` (`MEDNR`, `DATAUS`, `ZEITAUS`, `DATRUECK`, `ZEITRUECK`, `DATVERL`, `ZEITVERL`, `ANZVERL`, `KNDNR`, `MTYP`, `FILNRAUS`, `TNRAUS`, `FILNRRUECK`, `TNRRUECK`, `FILNRVERL`, `TNRVERL`, `STATAUS`, `STATRUECK`);
CREATE TABLE `DBF_database_library_DAAPROTX_dbf` (`PROTTYP`, `MEDNR`, `DATUM`, `ZEIT`, `KNDNR`, `FILNR`, `TNR`, `STATION`);
CREATE TABLE `DBF_database_library_DAAUS_dbf` (`AUSNR`, `KNDNR`, `MEDNR`, `MEDNRX`, `DATUM`, `ZEIT`, `DAT0`, `DAT1`, `MAHN1`, `DAT2`, `MAHN2`, `DAT3`, `MAHN3`, `DAT4`, `MAHN4`, `DAT5`, `MAHN5`, `ANZVERL`, `SUMBETRAG`, `DATBER`, `TNR`, `FILNR`, `STATION`);
CREATE TABLE `DBF_database_library_DAAUTOR_dbf` (`AUTORNR`, `NAME`, `VNAME`, `NNAME`);
CREATE TABLE `DBF_database_library_DAAUTORX_dbf` (`AUTORNR`, `ORT`, `WIRKORT`, `GEBDAT`, `GEBORT`, `TODDAT`, `TODORT`, `BEM1`, `BEM2`, `BEM3`);
CREATE TABLE `DBF_database_library_DABLZ_dbf` (`BLZ`, `LOESCH_DAT`, `BEZ_BANK`, `KURZ_BEZ`, `PLZ`, `ORT`, `BEZ_BTXEZU`, `INSTNR_PAN`, `BLZ_MERKMA`, `BIC`, `KENNZIFFER`, `SATZ_NR`);
CREATE TABLE `DBF_database_library_DAFIL_dbf` (`FILNR`, `FILKRZ`, `FILBEZ`, `FILBEZ2`, `FILSTR`, `FILORT`, `FILTOUR`, `FILKZ1`, `FILKZ2`, `FILKZ3`, `FILKZ4`);
CREATE TABLE `DBF_database_library_DAFLL_dbf` (`WORT`, `KZSORT`);
CREATE TABLE `DBF_database_library_DAFTAG_dbf` (`BEZ`, `DATUM`, `TYP`);
CREATE TABLE `DBF_database_library_DAKASSE_dbf` (`KNDNR`, `WRG`, `BETRAG`, `TEXT`, `DATUM`, `TYP`, `KTO`, `FILNR`, `TNR`, `STATION`);
CREATE TABLE `DBF_database_library_DAKND_dbf` (`KNDNR`, `NAME1`, `STRASSE1`, `PLZ`, `ORT`, `ORTSTEIL`, `STRASSE`, `STADTZIP`, `LAND`, `SNAME`, `NAME`, `VORNAME`, `KSTNR`, `ABTEILUNG`, `DATPASSIV`, `TELP`, `TELD`, `KZMW`, `ANREDE`, `AUSWEIS`, `GEBDAT`, `BERUF`, `AUSW1BIS`, `AUSW2BIS`, `AUSWDAT`, `AUSWBETRAG`, `BEM1`, `BEM2`, `KZINFO`, `EINTRDAT`, `AUSTRDAT`, `LETBES`, `ANZMAHN`, `BEITRGRP`, `KTOSTD`, `SPERRUNG`, `ANZAM`, `ANZAJ`, `ANZVJ`, `KZ1`, `KZ2`, `KZ3`, `KZ4`, `KZHIST`, `AENDDAT`, `AEND`, `AUSWAHL`, `SELEKTION`, `PASSW`, `KNDNRX`, `SESSIONID`, `IOPACPW`, `IOPACDATE`, `IOPACTIME`, `IOPACLOGS`, `IOPACSPERR`, `INOLOGIN`, `EMAIL`, `KZEMAIL`);
CREATE TABLE `DBF_database_library_DAKNDK_dbf` (`KNDNR`, `KRZ`);
CREATE TABLE `DBF_database_library_DAKNDX_dbf` (`KNDNR`, `WRG`, `BETRAG`, `TEXT`, `DATUM`, `TYP`, `KTOSTD_ALT`, `MEDNR`, `KTO`);
CREATE TABLE `DBF_database_library_DAKURZ_dbf` (`KNR`, `BEZ`, `ANZAHL`, `KNRX`);
CREATE TABLE `DBF_database_library_DALCK_dbf` (`TAETIGKEIT`, `ZAHL`);
CREATE TABLE `DBF_database_library_DAMAB1_dbf` (`FLDNR`, `INDIK`, `BEZ`, `FIX`, `BEM1`, `BEM2`, `BEM3`, `BEM4`, `BEM5`, `VARN`, `LEN`, `LENGET`, `KZSTW`, `KZIMP`, `KZEXP`, `KZDUPL`, `KZVORGABE`, `VORGABE`, `KZSORT`, `ANZAHL`, `MABFLD2`);
CREATE TABLE `DBF_database_library_DAMAH_dbf` (`KNDNR`, `DMPORTO`, `GEBUCHT`, `MSTUFE`, `AUSDRUCKEN`, `SUMBETRAG`);
CREATE TABLE `DBF_database_library_DAMAHX_dbf` (`KNDNR`, `MEDNR`, `MSTUFE`, `DATUM`, `BETRAG`, `SUMBETRAG`, `AUSDRUCKEN`, `BERECHNEN`, `AUSNR`, `MEDNRX`, `MTYP`);
CREATE TABLE `DBF_database_library_DAMEDB_dbf` (`MEDBNR`, `MEDNR`, `MEDNRX`, `DEWEYNR`, `STANDORT`, `ANSCHDAT`, `AENDDAT`, `AEND`, `KNDNR`, `STATUS`, `LEIHVOR`, `LETAUSLEIH`, `ANZMONAT`, `ANZJAHR`, `ANZVJAHR`, `PREIS`, `FILNR`, `BIBNR`, `BEM1`, `FSK`, `INVNR`);
CREATE TABLE `DBF_database_library_DAMED_dbf` (`MEDNR`, `MEDNRX`, `AUTOR`, `TITEL`, `TITEL2`, `DEWEYNR`, `STANDORT`, `STYP`, `SNUMMER`, `HETYP`, `HEBENE`, `OST`, `ERSCHJAHR`, `ERSCHORT`, `VERLHERST`, `ANSCHDAT`, `AENDDAT`, `AEND`, `KNDNR`, `SEIT_MIN`, `EXEMP`, `STATUS`, `LEIHVOR`, `LETAUSLEIH`, `ANZMONAT`, `ANZJAHR`, `ANZVJAHR`, `LETRES`, `ANZRES`, `ANZRESAJ`, `ANZRESVJ`, `FSK`, `PREIS`, `MTYP`, `FILNR`, `BSTNR`, `LFTNR`, `EAN`, `USERUPDATE`, `AENDUSER`);
CREATE TABLE `DBF_database_library_DAMEDMAB_dbf` (`MEDMNR`, `MEDNR`, `HEBENE`, `STYP`, `FLDNR`, `INDIK`, `TXT1`, `KZSORT`);
CREATE TABLE `DBF_database_library_DAMEDX_dbf` (`MEDNR`, `KNR`);
CREATE TABLE `DBF_database_library_DAMEDY_dbf` (`MEDNR`, `FLDNR`, `INDIK`, `TXT1`);
CREATE TABLE `DBF_database_library_DAPLZ_dbf` (`PLZ`, `ORT`, `BLNR`, `VORWAHL`);
CREATE TABLE `DBF_database_library_DAPROT_dbf` (`MEDNR`, `MTYP`, `AUTOR`, `TITEL`, `DEWEYNR`, `ANSCHDAT`, `DATUM`, `INFO`, `FLD1`, `FLD2`, `FLD3`, `FLD4`, `FLD5`, `STATUS`, `LEIHVOR`, `LETAUSLEIH`, `ANZMONAT`, `ANZJAHR`, `ANZVJAHR`, `FILNR`);
CREATE TABLE `DBF_database_library_DAQRY_dbf` (`QRYNR`, `BEZ`, `ERFDAT`, `AENDDAT`, `ERFUSER`, `AENDUSER`);
CREATE TABLE `DBF_database_library_DAQRYX_dbf` (`QRYNR`, `CONT_NAME`, `CONT_VALUE`, `CONT_TYPE`);
CREATE TABLE `DBF_database_library_DARES_dbf` (`RESNR`, `KNDNR`, `MEDNR`, `DATUM`, `ZEIT`, `STATUS`, `AUSWAHL`, `ABHOLEN`, `SENDDAT`, `SENDBEM`, `DELDAT`, `STICHW`, `DEWEYNR`, `ISBN`, `TNR`, `FILNR`, `STATION`);
CREATE TABLE `DBF_database_library_DASTW_dbf` (`STICHW`, `MEDNR`, `VARN`);
CREATE TABLE `DBF_database_library_DASYN_dbf` (`KNR`, `KNRSYN`);
CREATE TABLE `DBF_database_library_DASYS_dbf` (`DEWEYNR`, `SYSTYP`, `TXT`, `SYSART`, `ANZAHL`, `SYSLEVEL`, `DEWEYNRX`, `BEM1`, `BEM2`, `BEM3`, `BEM4`, `BEM5`, `BEM6`);
CREATE TABLE `DBF_database_library_DASYSK_dbf` (`DEWEYNR`, `KNR`, `BEM`);
CREATE TABLE `DBF_database_library_DATOU_dbf` (`TNR`, `TKRZ`, `TBEZ`, `TKZ1`);
CREATE TABLE `DBF_database_library_DATOUX_dbf` (`TNR`, `FILNR`, `STARTZEIT`, `ENDEZEIT`, `STARTDAT`, `ENDEDAT`);
CREATE TABLE `DBF_database_library_DATOUXX_dbf` (`TNR`, `TDAT`);
CREATE TABLE `DBF_database_library_DAWORT_dbf` (`WORT`, `ANZAHL`);
CREATE TABLE `DBF_database_library_DBVLG_dbf` (`VLGNR`, `ISBNNR`, `NAME1`, `NAME2`, `STRASSE`, `LAND`, `PLZ`, `ORT`, `TELEFON`, `TELEX`, `TELEFAX`, `SB1`, `SB2`, `KTONR`, `BLZ`, `BBZ`, `VART`, `ZZ1`, `ZZ2`, `SK1`, `DMAM`, `DMAJ`, `DMVJ`, `XKNDNR`, `BRANCHE`, `SELEKTION`, `FIBU`, `VERLAG`);
CREATE TABLE `DBF_database_library_DYKRZ_dbf` (`KRZNR`, `FELD`, `KRZ`, `TXT`, `FEST`, `USER01`, `USER02`, `POSNR`);
Ich habe versucht so gut wie möglich den Sinn der einzelnen Tabellen zu entschlüsseln und aufzuschreiben. Einige Tabellen enthielten keinen Inhalt, was, wie sich später herausstellte, an einem etwas fehlerhaften Konvertierung nach sqlite lag. Da ich es nicht geschafft habe alle Tabellen nach sqlite zu konvertieren sind manche mit leer bezeichnet, die eigentlich etwas enthalten, aber nicht konvertiert haben. Die nicht konvertierten Tabellen waren für mich nicht wichtig. Die besonders wichtigen Dateien habe ich fett hervorgehoben. Die Durchgestrichenen sind Tabellen die einfach nur Bankleitzahlen, Ferientage oder Verlage auflisten.
DAAPROTX Vermutlich ausleihe und Zurückgabetabelle 1. PROTTYP vermutlich ProzessTyp
DAAPROT Tabelle die ausleihen und Rückgaben enthält
DAAUS Ausleihen mit Mahnung und "Gebühren"
DAAUTORX Leer
DAAUTOR Name, Vorname und Nachname von Autoren (ohne id)
~~DABLZ Bankleitzahlenverzeichnis~~
DAFIL Leer
DAFLL Leer
~~DAFTAG Ferientage~~
DAKASSE Kassen Einzahlungen (2010)
DAKNDK Leer
DAKNDX Leer
DAKND Nutzer mit Namen und Kundennummern usw.
DAKURZ Kurztitel für Bücher? (MEDNR <-> KNR mit DAMEDX)
DALCK Höchstwerte für die Nummern
DAMAB1 Bedeutung der MAB Mappings
DAMAHX Mahnungsverzeichnis
DAMAH Mehr Mahnungen
DAMEDB Mediendatenbank, enthält Kürzel(DEWEYNR), Barcode(mednrx) Anschaffungsdatum MEDNR usw.
DAMEDMAB MAB der Medien Quelle für MARC21
DAMEDX - Mapping Medium to KeywordID (MEDNR - KNR)
DAMEDY - Addition of Summaries
DAMED Exemplarinformation MEDNR, Titel, Untertitel, Autor, Preis, ...
DAPLZ Leer
DAPROT weniger ausführliche Exemplar Information...
DAQRYX Keine Ahnung scheint unwichtig
DAQRY Suchverzeichnis
DARES Reservierungen
DASTW Stichwortverzeichnis für die Medien
DASYN Leer
DASYSK Leer
DASYS Systematik aufgeschrieben.
DATOUXX ? Unwichtig
DATOUX Leer
DATOU Leer
DAWORT Leer
~~DBVLG Verlagsverzeichnis~~
DYKRZ Kurznummernverzeichnis (1-232) für Medientypen
Mit diesem Wissen bewaffnet konnte ich mich dann daran machen zu sehen, wie ich diese Daten bei Koha importieren kann. Zunächst die bibliografischen Daten, also die Metadaten zu den Büchern.
Konvertieren der Datenbank nach sqlite
Da ich mit DBF keine Erfahrung hatte und ich die Bibliotheken dafür recht umständlich fand habe ich als erstes die DBF Datenbank zu einer Sqlite Datenbank konvertiert. Da die Quelldaten nicht besonders gut waren musste ich das Pythonmodul anpassen und konnte dann so alles wichtige exportieren.
Öffnen der Datenbank in Python
Das öffnen der Sqlite Datenbank ist recht einfach möglich. Da der Wörterbuchzugriff am einfachsten ist, generiere ich ein dictionary je Zeile damit lässt sich auf die einzelnen Werte der Zeilen mit l['fldnr']
zugreifen. Dann wird einfach über die Zeilen iteriert.
### Read the sqlite database as a dictionary.
def dict_factory(cursor, row):
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
conn = sqlite3.connect('sqlite.db'))
conn.row_factory = dict_factory
with conn:
c = conn.cursor()
# Process the information from damedmab.
for l in c.execute('SELECT * FROM DBF_database_damedmab_dbf'):
# iterate over all the lines
Im folgenden werden aus diesen Zeilen die MARC21
records erstellt. Immer mehrere Zeilen der damedmab
Tabelle ergeben einen MARC21
record.
Erstellen einer bibliografischen Datenbank in MARC21
Die Quelldaten sind in dem früher in Deutschland üblichen Format MAB2
gespeichert. Koha verwendet intern und für den Import den weltweit verbreiteten Standard MARC21
dieser ist zunächst recht kompliziert und man muss viele Zahlen und Buchstaben Kombinationen nachschlagen. Für das Nachschlagen habe ich folgende Seiten verwendet:
Und netterweise auch eine konversionshilfe, also was in MAB2 welchem in MARC21 entspricht: https://d-nb.info/988382318/34
Bei genauerem Hinsehen geht es jedoch sehr schnell immer passendes zu finden. Für die meisten Tags war es bei mir ausreichend in der letzten Datei per ([strg]+[f]
) Volltextsuche die richtige Zeile zu suchen, und dann das zu übernehmen, was die Deutsche Nationalbibliothek für gut heist. Nur manchmal ist es nicht ganz eindeutig, dann muss man nochmal die eigenen Daten anschauen und die Feldspezifikationen um zu entscheiden wie man das alles konvertiert.
Der grundsätzliche Aufbau von beiden Speicherformaten:
Soll zum Beispiel der Autor abgespeichert werden, so wird durch eine Zahl, oder eine Zahlen- und Buchstabenkombination quasi der Typ des abgespeicherten Wertes festgelegt. Library for Windows hat diese Daten in der Tabelle DAMEDMAB
gespeichert.
Beispiel 1:
Der Erscheinungsort wird in MAB2
unter dem Tag 410
gespeichert. In MARC21
ist das der Tag 260
. Da immer wieder für alle möglichen unterschiedlichen Tags verschiedene Herangehensweisen notwendig waren, habe ich einfach sehr viele if
s und else
s erstellt, um jedem alten Tag seinen richtigen neuen Wert zuweisen zu können.
Für den Erscheinungsort sieht dieses if
dann so aus:
elif l["fldnr"] == '410':
log("** Erscheinungsort", l["txt1"])
# Erscheinungsort
records[l['mednr']].add_field(Field(
tag = '260',
indicators = [' ',' '],
subfields = [
'a', l["txt1"]
]))
Die Indikatorten indicators
und Unterfelder subfields
werden dazu verwendet, noch mehr Daten unter dem Tag 260
zu speichern.
So ist zum Beispiel der Erscheinungs-Verlag bei MAB2
unter dem Tag 412
gespeichert, bei MARC21
dagegen in dem Unterfeld (subfield
) b
des 260
Tags.
elif l["fldnr"] == '412':
log("** Erscheinungsverlag", l["txt1"])
# ErscheinungVerlag
if '260' in records[l['mednr']]:
records[l['mednr']]['260'].add_subfield('b', l["txt1"])
else:
records[l['mednr']].add_field(Field(
tag = '260',
indicators = [' ',' '],
subfields = [
'b', l["txt1"]
]))
Das Erscheinungsdatum muss von 425
ins Unterfeld c
von 260
.
elif l["fldnr"] == '425':
log("** Erscheinungsdatum", l["txt1"])
# Erscheinungsdatum
if '260' in records[l['mednr']]:
records[l['mednr']]['260'].add_subfield('c', l["txt1"])
else:
records[l['mednr']].add_field(Field(
tag = '260',
indicators = [' ',' '],
subfields = [
'c', l["txt1"]
]))
Beispiel 2:
Der Titel eines Werkes hat in MAB2
den fldnr
-Wert 331
. Für MARC21
muss der Titel in dem Feld 245
abgespeichert werden. Für den Titel sieht das dann so aus:
elif l["fldnr"] == '331':
log("**Main title ", l["txt1"])
# Main title
if not '245' in records[l['mednr']]:
log(" adding a" + l["txt1"])
records[l['mednr']].add_field(Field(
tag = '245',
indicators = ['1','0'],
subfields = [
'a', l["txt1"],
]))
elif not 'a' in records[l['mednr']]['245']:
log(" adding sub a" + l["txt1"])
records[l['mednr']]['245'].add_subfield('a', l["txt1"])
elif not 'g' in records[l['mednr']]['245']:
log(" adding sub g" + l["txt1"])
records[l['mednr']]['245'].add_subfield('g', l["txt1"])
else:
log(" extending g" + l["txt1"])
records[l['mednr']]['245']['g'] = records[l['mednr']]['245']['g'] + '–' + l["txt1"]
Da im Format MAB2
Mehrmals das Feld 331
vorkommen kann, in MARC21
aber die Haupttitel im Unterfeld a
und die Untertitel im Unterfeld g
gespeichert werden, musste hier noch einiges an Zusatzfällen geregelt werden.
Es hat sich hier auch nicht immer genau automatisch bestimmen lassen, welches der Haupt und welches der Untertitel ist. MAB2
regelt das vermutlich über die Reihenfolge.
In meinem Skript habe ich die Tags die ich noch nicht zugewiesen habe in ein dictionary gespeichert notimpl
. stößt das Skript also auf einen Tag zu dem es nicht weiß was es tun soll wird es dort hinzu gefügt, und dann am Ende werden alle Tags ausgegeben, die noch bearbeitet werden müssen.
Im letzten teil werden die MARC21
Einträge gespeichert - bis jetzt war ja alles nur im Ram.
print("writing to file marcout.mrc")
with open('marcout.mrc', 'ab') as o:
for k in records:
o.write(records[k].as_marc())
Das wäre theoretisch alles um die Titelinformationen komplett zu haben. Nur wie man sehen kann existiert noch eine ganze Menge weiterer Code. Ein großer Teil ist um die Felder zu vereinheitlichen und Fehler in den Quelldaten zu korrigieren. Da Schulbibliotheken meist nicht von ausgebildeten Bibliothekaren und in wechselnder Besetzung geführt werden sind die Daten eher durchwachsen. Was ich automatisch lösen konnte habe ich versucht automatisch zu lösen. Bei einer anderen Bibliothek werden aber hier sicherlich ganz andere Probleme auftreten.
Die Exemplardaten
Ein weiterer großer Teil der noch ergänzt wird sind die Exemplardaten. Bis jetzt sind nur die Titel der Bücher, nicht aber die Anzahl und Barcodes der einzelnen Exemplare konvertiert. Das Zielsystem Koha erwartet die Exemplarinformationen in dem Tag 952
. Hier war es praktischer durch die schon existierenden Titeldaten zu gehen, und für jeden Titel die Exemplarinformation mit einem SQL-Query zu holen. Wie Koha die daten erwartet ist hier dokumentiert: https://wiki.koha-community.org/wiki/Holdings>datafields_(9xx)
Den Rest hinzu fügen
Für unsere Bibliothek konnte man noch Schlüsselwörter hinzu fügen. Außerdem habe ich noch die Systematik verwendet um noch mehr Schlüsselwörter zu generieren, sodass die Bücher schonmal eine gute Ordnung haben und gut durchsuchbar sind, wenn sie in Koha importiert werden.
Wenn soweit alles richtig ist, dann kann das script ausgeführt werden und die ausgegebene marcout.mrc
Datei kann in Koha importiert werden und der Bibliotheksbetrieb kann losgehen.
Wer Fragen hat darf sich gerne bei mir melden siehe Kontakt und Impressum ↓.
Kommentare
Einen Kommentar hinzufügen
Alle Kommentare werden moderiert, das heißt die Kommentare werden erst auf der Seite erscheinen, wenn ich Zeit gefunden habe sie anzusehen.
In den Kommentaren kann markup syntax verwendet werden.