Wie dieser Web-Auftritt erzeugt wird
Vorhaben
Mit diesem Programm werden Web-Seiten anhand einer Muster-, einer Struktur-Datei sowie von Inhaltsdateien erzeugt. Dabei wird anhand der Struktur-Datei auch die Navigation eingebaut.
Verzeichnisstruktur
Unter einem Projekt-Verzeichnis werden die Unterverzeichnisse 'eingabe' und 'ausgabe' benötigt. In das Verzeichnis 'eingabe' sind die Dateien "Struktur.xml", "muster.txt" sowie die Dateien mit den Inhalten ("*.inh") einzustellen.
Im Verzeichnis "ausgabe" sind Verzeichnisse für jedes Inhaltsthema einzurichten, für das eine Seite oder mehrere Seiten angelegt werden sollen.
- Garten
- Katzen
- Python
- Sonstiges
- Formelles
Üblicherweise benötigen Web-Seiten zusätzliche Ressourcen wie Bilder und Formatierungsanweisungen.
Formatierungsanweisungen gelten regelmäßig für den gesamten Web-Auftritt. Hierfür ist im Verzeichnis "ausgabe" das Unterzeichnis "assets" und darunter beispielsweise das Verzeichnis "css" anzulegen. Das Programm erzeugt hierfür relative Verweise.
Für Bilder usw. wird jeweils ein entsprechender Ordner innerhalb des Inhaltsthemas angelegt, so daß die in den Inhaltsdateien enthaltenen relativen Verweise unverändert in die fertige Web-Seite übernommen werden können.
Struktur-Datei
Die Struktur-Datei Struktur.xml beschreibt die Struktur des gesamten Web-Auftritts.
<?xml version="1.0" encoding="UTF-8"?> <WebAuftritt> <Seite Name="index.html" Titel="Web-Auftritt der Familie Frerich" Kopfzeile="Über uns" Navi="Start" Inhalt="Start.inh"/> <Seite Name="Katzen/index.html" Kopfzeile="Unsere Mitbewohner" Navi="Katzen" Inhalt="KatzenIndex.inh"> <Seite Name="Katzen/MuM.html" Kopfzeile="Maja und Mohrle" Navi="Unsere Heutigen" Inhalt="aktuelleKatzen.inh"/> <Seite Name="Katzen/Katzenerlebnisse.html" Kopfzeile="Unsere Katzen" Navi="...und mehr" Inhalt="Katzenerlebnisse.inh"/> <Seite Name="Katzen/fruehere.html" Titel="So war der Lauf der Zeit" Navi="Frühere" Inhalt="fruehere.inh"/> </Seite> <Seite Name="Garten/index.html" Titel="Unser Garten" Kopfzeile="Unser Garten" Navi="Garten" Inhalt="GartenIndex.inh"/> <Seite Name="Python/index.html" Titel="Arbeiten mit Python" Kopfzeile="Arbeiten mit Python" Navi="Python"/> <Seite Name="Sonstiges/index.html" Titel="Bunt Gemischtes" Kopfzeile="Bunt Gemischtes" Navi="Sonstiges" Inhalt="Sonstiges.inh"> <Seite Name="Sonstiges/Hifa2014Bardowick.html" Kopfzeile="Himmelfahrtstour 2014, erster Tag" Navi="Hifa 2014: 29.5.2014" Inhalt="Himm2014Bardo.inh"/> <Seite Name="Sonstiges/Hifa2014Bardowick.html" Kopfzeile="Himmelfahrtstour 2014, zweiter Tag" Navi="Hifa 2014: 30.5.2014" Inhalt="Himm2014Heide.inh"/> <Seite Name="Sonstiges/FussballWMeinzelne.html" Kopfzeile="Einzelne Münzen zu Fußball-Weltmeisterschaften" Navi="WM-Münzen" Inhalt="FussballWM.inh"/> <Seite Name="Sonstiges/Spanien.html" Kopfzeile="Münzen" Navi="WM-1982 Münzen" Inhalt="MzFussballWM1982.inh"/> <Seite Name="/Sonstiges/Leichtathletik1982.html" Kopfzeile="Münzen" Navi="Athletik-Münzen" Inhalt="MzLeichtathle1982.inh"/> </Seite> <Seite Name="Formelles/index.html" Titel="Formelles" Kopfzeile="Formelles" Navi="Formelles" Inhalt="Formelles.inh"/> </WebAuftritt>
In "Name" (= Seitenbezeichnung der HTML) darf nicht der Begriff aus "Navi" enthalten sein. (Siehe Hinweis unter der Programmauflistung)
Für jedes zu erzeugende Seiten-Skelett ist ein Element 'Seite' vorhanden. Ggf. sind die Elemente hierarchisch angeordnet (im Beispiel ist der Bereich "Katzen" untergliedert).
Das Seiten-Element kann folgende Attribute haben:
Attribut Bedeutung Name Name der Ausgabedatei Titel wird zum 'title'-Eintrag Kopfzeile wird zum 'h1'-Eintrag Navi Text für den Navigations-Querverweis
Fehlt das Titel-Attribut, wird der Wert von Kopfzeile hierfür angesetzt. Ist das Kopfzeile-Attribut nicht vorhanden, wird hierfür der Titel-Wert herangezogen. Fehlen beide Attribute, bleiben die Felder im Skelett leer.
Inhalte-Datei
Für jede Seite im Web-Auftritt wird eine Inhalte-Datei mit der Endung "inh" im Verzeinis "eingabe" angelegt.
Diese Dateien sind als XML aufgebaut und enthalten die Abschnitte:
- Inhalt
- Stichworte
- Beschreibung
- Zusammenfassung
Der Abschnitt "Inhalt" wird nach "body" der Web-Seite übernommen, die übrigen Abschnitte in den Teil "head" eingebaut.
Beispiel:
<Inhalt> <p> </p> <p>Wir sind ein nicht mehr ganz so junges Ehepaar, da sich für alles Mögliche interessiert, die verfügbare Zeit uns aber doch Beschränkungen beschert.</p> <p>Wesentlich sind drei Dinge, über die wir auch berichten wollen:</p> <ul> <li>Katzen</li> <li>Garten</li> <li>Computerei</li> </ul> <p>Daneben gibt es noch viele weitere interessante Themen. Und wenn wir hierüber etwas mitteilen wollen, packen wir es in den Abschnitt "Sonstiges".</p> </Inhalt> <Stichworte> Katze, Katzen, Python, Garten, Epilepsie </Stichworte> <Beschreibung> Übersicht über den Web-Auftritt </Beschreibung> <Zusammenfassung> Behandelte Themen in diesem Web-Auftritt </Zusammenfassung>
Muster-Datei
Die Muster-Datei muster.txt enthält den grundsätzlichen Aufbau einer einzelnen Seite des Web-Auftritts.
<!DOCTYPE html> <head> <title>@Titel</title> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="copyright" content="Egon Frerich" /> <meta name="keywords" content="@Stichworte" /> <meta name="description" content="@Beschreibung" /> <meta name="abstract" content="@Zusammenfassung" /> <meta name="revisit-after" content="1 month" /> <meta name="robots" content="all" /> <meta name="content-language" content="de" /> <meta name="author" content="Egon Frerich" /> <meta name="publisher" content="Egon Frerich" /> <link rel="stylesheet" href="assets/css/normalize.css"> <link rel="stylesheet" href="assets/css/fre.css" type="text/css" /> <script type="text/javascript" src="bilder/GM_Utils/GPX2GM.js"></script> </head> <body> <div id="page_margins"> <div id="page"> <header> <div id="topnav"> <a class="skip" href="#nav" title="Direkt zur Navigation springen">Zur Navigation springen</a> <a class="skip" href="#content" title="Direkt zum Inhalt springen">Zum Inhalt springen</a> <h1>@Kopfzeile</h1> </div> <div id="nav"> <div id="nav_main"> @NaviEins </div> </div> </header> <!-- Hauptteil --> <div class="container" id="content"> <div class="row"> <!-- Seitenleiste links --> <div class="three columns"> <p> </p> @NaviZwei </div> <!-- Inhalt --> <div class="eight columns"> @Inhalt </div> <!-- Ende Hauptteil --> </div> <!-- Fusszeile --> <div class="footer"> <div class="two columns"> <p class="small"><a href="http://validator.w3.org/check?uri=referer"><img src="http://www.frerich.eu/Icons/valid-xhtml10.png" alt="HTML-Validator" /> </a></p> </div> <div class="one column"> <p class="small"><a href="http://jigsaw.w3.org/css-validator/check/referer"> <img src="http://jigsaw.w3.org/css-validator/images/vcss" alt="CSS ist valide!" /> </a></p> </div> <div class="four columns"> <p> Version: 2.3 - © Egon Frerich</p> </div> </div> </div> </div> </body> </html>
Das Programm
Anhand der Struktur-Angaben werden die Muster-Angaben anhand der Informationen in den Inaltsdateien in Web-Seiten umgesetzt.
# -*- coding: utf8 -*- u""" Modul zum Erzeugen von Html-Skelett-Seiten Die Struktur-Datei wird mit Code untersucht, der von Zope 3 zur Auswertung von ZCML-Anweisugnen stammt und abgewandelt ist. """ import sys from xml.sax import make_parser from xml.sax.xmlreader import InputSource from xml.sax.handler import ContentHandler, feature_namespaces from xml.sax import SAXParseException from string import find, letters import codecs fileencoding = "utf-8" class AufbereitungsHandler(ContentHandler): """Schnittstelle zum XML Parser Parser-Events führen zum Anlegen von Einträgen in einer Skelett-Tabelle. """ def __init__(self): self.depth = 0 self.Seitenangaben = [] def setDocumentLocator(self, locator): self.locator = locator def startElementNS(self, name, qname, attrs): """ name name of the element type as a (uri, localname) tuple qname raw XML 1.0 name in source document attrs instance AttributesNS mit den Attrbuten des Elements Diese Datei kennt nur zwei Elementarten: - WebAuftritt - Seite Für jedes Element wird ein Dictionary angelegt. Für jedes Attribut wird sein Name mit einem vorgesetzten '@' und seinem zugehörigen Wert aufgenommen. Außer den in der XML-Datei vorhandenen Daten werden zwei weitere Einträge erzeugt: - Tiefe - @TiefRef 'Tiefe' gibt an, in welcher Tiefe das Element in der XML-Datei angeordnet ist. 'WebAuftritt' liegt auf Ebene 1, die Seitenelemente liegen entsprechend ihrer Verschachtelung darunter. Die 'Tiefe' bestimmt die Anordnung in der Navigation. Mit '@TiefRef' wird die hierarchische Tiefe der zu erzeugenden Dateien angezeigt. """ self.depth += 1 data = {'Tiefe':self.depth} for (ns, aname), value in attrs.items(): if ns is None: aname = '@'+str(aname) data[aname] = value if aname == '@Name': anz = value.count('/') data['@TiefRef'] = anz self.Seitenangaben.append(data) def holenSeitenangaben(self): return self.Seitenangaben def endElementNS(self, name, qname): self.depth -= 1 def processxmlfile(file): """ Aus XML-Datei Steuerungsdaten für die Web-Seiten-Skelette herausholen Hier wird der Vorgang angestoßen, das Herausholen selbst geschieht je Element mit dem Aufbereitungshandler """ src = InputSource(getattr(file, 'name', '<string>')) src.setByteStream(file) parser = make_parser() ah =AufbereitungsHandler() parser.setContentHandler(ah) parser.setFeature(feature_namespaces, True) try: parser.parse(src) except SAXParseException: print sys.exc_info()[1], sys.exc_info()[2] return ah.holenSeitenangaben() def processMuster(f): Muster = [] for Zeile in f: Muster.append(Zeile.decode(fileencoding)) return Muster NaviDaten = [] def vorbereitenNavi(Seitenangaben): """ Eintragungen für die Navigationsleisten heraussuchen Es gibt zwei Navigationsleisten, eine Kopf- und eine Seiten-Leiste. Die Kopfleiste enthält nur Eintragungen für die obere Ebene, d.h., für die unmittelbar im Root angesiedelten Dateien sowie Verweise auf die darunterliegenden Ordner. Die Seiten-Navigationsleiste ist zweistufig. Beim gegenwärtigen Ausbau ist die erste Stufe identisch mit der Kopf-Leiste. """ LfdNr = 0 for Seite in Seitenangaben: if Seite.has_key('@Navi'): NaviDaten.append([Seite['Tiefe'], LfdNr, Seite['@Navi']]) LfdNr += 1 def erzeugenReferenz(aufSeite, nachSeite): if aufSeite['@TiefRef'] == 0: # von oberster Ebene einfach mit ./ referenzieren Ref = u'./' + nachSeite['@Name'] elif nachSeite['@TiefRef'] == 0: # Referenzierte Seite liegt auf oberster Ebene Ref = u'../' + nachSeite['@Name'] else: # beide Seiten befinden sich in der zweiten Hierarchiestufe Stelle1 = nachSeite['@Name'].find(u'/') VerzNameReferenz = nachSeite['@Name'][0:Stelle1] Stelle2 = aufSeite['@Name'].find(u'/') VerzNameSeite = aufSeite['@Name'][0:Stelle2] if VerzNameReferenz == VerzNameSeite: # liegt im gleichen Verzeichnis # Schrägstrich von Namen Ref = u'.' + nachSeite['@Name'][Stelle1:] else: Ref = u'../' + nachSeite['@Name'] return Ref def erzeugenNavi(Seitenangaben, Seite): """ Navigationsdaten für eine Seite erzeugen Je nach Lage der Seite im Dateisystem sind die Referenzen zu anderen Seiten zu erzeugen. Liegt die referenzierte Seite im gleichen Verzeichnis, lautet die relative Referenz './'. Befindet sich die referenzierte Seite im unmittelbaren Verzeichnis darüber, ist mit '../' zu referenzieren. Eine weitere hierarchische Stufe ist in dieser Version nicht eingebaut. """ Ebene = 2 ErsteZeile = True Navi2 = [u'<ul>\n'] Navi1 = [u'<ul>\n'] if Seite['@TiefRef'] == 0: # keine höhere Hierarchie vorhanden Ref = u'./' else: # ggf. höhere Hierarchie ansteuern Ref = u'../' for Tiefe, LfdNr, Text in NaviDaten: Ref = erzeugenReferenz(Seite, Seitenangaben[LfdNr]) if Tiefe == Ebene: if not ErsteZeile: Navi2.append(Beginn + u'</li>\n') Beginn =u'<li><a href="'+Ref+u'">' + Text + u'</a>' if Ebene == 2: Navi1.append(Beginn + u'</li>\n') ErsteZeile = False elif Tiefe > Ebene: # alten Verweis abschließen und neue Gruppe beginnen Navi2.append(Beginn + u'\n<ul>\n') Ebene += 1 Beginn =u'<li><a href="'+Ref+u'">' + Text + u'</a>' ErsteZeile = False else: Navi2.append(Beginn + u'</li>\n</ul>\n</li>\n') Ebene -= 1 Beginn =u'<li><a href="'+Ref+u'">' + Text + u'</a>' if Ebene == 2: Navi1.append(Beginn + u'</li>\n') ErsteZeile = False Navi2.append(Beginn + u'</li>\n</ul>\n') Navi1.append(u'</ul>\n') return Navi1, Navi2 def aufbereitenNavi1(Navigationsbegriff, Navi1): Navi1Text = '' for Zeile in Navi1: Stelle = Zeile.find(Navigationsbegriff) if Stelle >= 0: Zeile = u'<li id="current"><a href="#">' + Navigationsbegriff + \ u'</a></li>\n' Navi1Text = Navi1Text + Zeile return Navi1Text def aufbereitenNavi2(Navigationsbegriff, Navi2): """ Bei der aktiven Seite die Referenz auf sie entfernen und den Navigationsbegriff für eine entsprechende Anzeige als 'active' kennzeichnen. Es sind folgende Fälle unterscheiden: - Navigationsbegriff steht auf der gleichen Ebene wie der vorhergehende und nachfolgende - Navigationsbegriff ist Oberbegriff für nachfolgende Menüeinträge - Navigationsbegriff ist letzter unter einem Oberbegriff - Navigationsbegriff steht auf der gleichen Ebene wie der vorhergehende, ein nachfolgender Navigationsbegriff ist nicht vorhanden Zur Beschreibung der Fälle dienen folgende Kürzel: - BegAufz = Beginn Aufzählung (<ul>) - EndAufz = Ende Aufzählung (</ul> - StartM = Start des Menüpunkts (<li>) - EndeM = Ende des Menüpunkts (</li> - Ref = Referenz mit Navigationsbegriff - act = Aktiver Menüeintrag (<li id="active">) - Navi = Navigationsbegriff - NL = Neue Zeile Im ersten Fall ändert sich der Ursprungseintrag wie folgt: StartM Ref EndeM NL ==> act EndeM NL Im zweiten Fall ändert sich der Ursprungseintrag wie folgt: StartM Ref NL BegAufz NL ==> act EndeM NL BegAufz NL Im dritten Fall ändert sich der Ursprungseintrag wie folgt: StartM Ref EndeM NL EndAufz NL EndeM NL ==> act NL EndAufz NL EndeM NL Im vierten Fall ändert sich der Ursprungseintrag wie folgt: StartM Ref EndeM NL EndAufz NL ==> act NL EndAufz NL Die in der 'rohen' Navigationsliste gesammelten Einträge werden nachfolgend als Zeilen aufgefaßt, enthalten aber auch NL-Zeichen. """ Navi2Text = '' for Zeile in Navi2: Stelle = Zeile.find(Navigationsbegriff) # Betrifft der Navigationsbegriff für die aktuelle Seite? if Stelle >= 0: # Ja Stelle2 = Zeile[Stelle:].find(u'</li>') # Erster, dritter oder vierter Fall? if Stelle2 == -1: # Nein - zweiter Fall Zeile = u'<li class="active">' + Navigationsbegriff + u'\n<ul>\n' else: Stelle2 = Zeile[Stelle:].find(u'</ul>') if Stelle2 == -1: # Erster Fall Zeile = u'<li class="active">' + Navigationsbegriff + \ u'</li>\n' else: Stelle3 = Zeile[Stelle+Stelle2:].find(u'</li>') if Stelle3 == -1: # vierter Fall Zeile = u'<li class="active">' + Navigationsbegriff + \ u'</li>\n</ul>\n' else: # dritter Fall Zeile = u'<li class="active">' + Navigationsbegriff + \ u'</li>\n</ul>\n</li>\n' Navi2Text = Navi2Text + Zeile return Navi2Text TiefenRef = {0: u'./', 1: u'../', 2: u'../../' } def erzeugenSeiten(Seitenangaben, Muster): """ """ LfdNr = 0 Verz ={} for Zeile in Muster: Stelle = find(Zeile, u'@') if Stelle >= 0: Stelle2 = Stelle + 1 while Zeile[Stelle2] in u'ABCDEFGHIJKLMONPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz': Stelle2 +=1 Verz[Zeile[Stelle:Stelle2]] = [LfdNr, Stelle, Stelle2] LfdNr += 1 vorbereitenNavi(Seitenangaben) for Seite in Seitenangaben: if Seite['Tiefe'] == 1 : pass else: # Muster kopieren mc = [] for Zeile in Muster: Stelle = Zeile.find(u'href="assets/') if Stelle >= 0: Zeile = Zeile[0:Stelle+6] + TiefenRef[Seite['@TiefRef']] + \ Zeile[Stelle+6:] mc.append(Zeile) # Navigation für diese Seite grob erzeugen Navi1, Navi2 = erzeugenNavi(Seitenangaben, Seite) # Informationen für diese Seite zusammenstellen Inhalte = {'@Inhalt': u'Diese Seite wird in Kürze gefüllt', '@Stichworte': u'', '@Beschreibung': u'', '@Zusammenfassung' : u''} if Seite.has_key('@Inhalt'): f = open('./eingabe/'+Seite['@Inhalt'], 'r') inhalt = f.read().decode(fileencoding) f.close() Stelle = inhalt.find(u'<Inhalt>') if Stelle >= 0: Stelle2 = inhalt.find(u'</Inhalt>') Inhalte['@Inhalt'] = inhalt[Stelle+9:Stelle2-1] Stelle = inhalt.find(u'<Stichworte>') if Stelle >= 0: Stelle2 = inhalt.find(u'</Stichworte>') Inhalte['@Stichworte'] = inhalt[Stelle+13:Stelle2-1] Stelle = inhalt.find(u'<Beschreibung>') if Stelle >= 0: Stelle2 = inhalt.find(u'</Beschreibung>') Inhalte['@Beschreibung'] = inhalt[Stelle+15:Stelle2-1] Stelle = inhalt.find(u'<Zusammenfassung>') if Stelle >= 0: Stelle2 = inhalt.find(u'</Zusammenfassung>') Inhalte['@Zusammenfassung'] = inhalt[Stelle+18:Stelle2-1] # Jeden Statthalter des Musters bearbeiten for Eintrag, Platz in Verz.items(): if Eintrag == '@Inhalt' or \ Eintrag == '@Stichworte' or \ Eintrag == '@Beschreibung' or \ Eintrag == '@Zusammenfassung': mc[Platz[0]] = mc[Platz[0]][0:Platz[1]] + \ Inhalte[Eintrag] + mc[Platz[0]][Platz[2]:] elif Seite.has_key(Eintrag): mc[Platz[0]] = mc[Platz[0]][0:Platz[1]] + \ Seite[Eintrag] + mc[Platz[0]][Platz[2]:] elif Eintrag == '@Titel': # für @Titel keine Angabe in der Seitenbeschreibung if Seite.has_key('@Kopfzeile'): # Kopfzeile vorhanden, als Titel nehmen mc[Platz[0]] = mc[Platz[0]][0:Platz[1]] + \ Seite['@Kopfzeile'] + \ mc[Platz[0]][Platz[2]:] else: pass elif Eintrag == '@Kopfzeile': # für @Kopfzeile keine Angabe in der Seitenbeschreibung if Seite.has_key('@Titel'): # Titel vorhanden, als Kopfzeile nehmen mc[Platz[0]] = mc[Platz[0]][0:Platz[1]] + \ Seite['@Titel'] + \ mc[Platz[0]][Platz[2]:] else: pass elif Eintrag == '@NaviEins': Navi1Text = aufbereitenNavi1(Seite['@Navi'], Navi1) mc[Platz[0]] = mc[Platz[0]][0:Platz[1]] + \ Navi1Text + \ mc[Platz[0]][Platz[2]:] elif Eintrag == '@NaviZwei': Navi2Text = aufbereitenNavi2(Seite['@Navi'], Navi2) mc[Platz[0]] = mc[Platz[0]][0:Platz[1]] + \ Navi2Text + \ mc[Platz[0]][Platz[2]:] else: pass f = open('./ausgabe/'+Seite['@Name'], 'w') for Zeile in mc: out = Zeile.encode('utf-8') f.write(out) f.close() if __name__ == "__main__": f = open(r'./eingabe/Struktur.xml', 'r') Seitenangaben = processxmlfile(f) f.close() f = open(r'./eingabe/muster.txt', 'r') Muster = processMuster(f) f.close() erzeugenSeiten(Seitenangaben, Muster)
Da der Navigationsbegriff nur mit "find" gesucht wird, sind in den Namen der zu erzeugenden HTML-Seiten keine Navigationsbegriffe zu verwenden.