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.