Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • v.mattfeld/fg-website
1 result
Show changes
.PHONY: run
run:
make -C ..
#!/usr/bin/env python3
import json
import os
import sys
import mimetypes
import common
import datatypes
import reader
import generator
import writer
#content_dir = '../content'
#output_dir = '../output'
#theme_dir = '../theme'
#config_file = '../config.json'
#lang_file = '../lang.json'
def main(content_dir, output_dir, theme_dir, config_file, lang_file):
print("Hello World")
config = {}
with open(config_file) as f:
config = common.combine(config, json.loads(f.read()))
with open(lang_file) as f:
if 'lang' not in config:
config['lang'] = {}
config['lang'] = common.combine(config['lang'], json.loads(f.read()))
with open(content_dir + '/config.json') as f:
config = common.combine(config, json.loads(f.read()))
print(config)
factories = {}
factories['page'] = datatypes.PageFactory(config, factories)
factories['author'] = datatypes.AuthorFactory(config, factories)
factories['tag'] = datatypes.TagFactory(config, factories)
factories['link'] = datatypes.LinkFactory(config, factories)
factories['config'] = datatypes.LocalizedConfigFactory(config, factories)
factories['file'] = datatypes.FileFactory(config, factories)
mimetypes.init()
readers = []
readers.append(reader.RawFileReader(config, factories))
for mimetype, mimeconfig in config['pandoc']['mimetypes'].items():
extensions = config['pandoc']['extensions']
if 'extensions' in mimeconfig:
extensions = mimeconfig['extensions']
readers.append(reader.PandocReader(config, factories, mimetype, None, base=mimeconfig['base'], extensions=extensions))
read_dir(os.path.join(content_dir, "."), readers)
read_dir(theme_dir + '/static', readers, [config['theme']['static_dir']])
context = {}
gen = generator.Generator(config, context, factories)
gen.generate_context()
wrt = writer.Writer(config, context, output_dir, theme_dir)
gen.generate_output(wrt)
def read_dir(directory, readers, subpath = []):
print("read_dir: " + directory);
for filename in os.listdir(directory):
if filename.startswith("."):
continue
f = os.path.join(directory, filename)
if os.path.isfile(f):
mimetype, mimeencoding = mimetypes.guess_type(f)
#print("mimetype:",f,mimetype,mimeencoding)
read_file = False
for reader in readers:
if reader.can_read_file(mimetype, mimeencoding):
print("read file: ", f, subpath, mimetype, mimeencoding, reader.__class__)
read_file = True
reader.read_file(f, subpath, mimetype, mimeencoding)
if not read_file:
print("WARN: no reader for file: ", f, subpath, mimetype, mimeencoding)
#readers['generic'].read_file(f, subpath)
#if filename.endswith(".md"):
# readers['md'].read_file(f, subpath)
elif os.path.isdir(f):
read_dir(f, readers, subpath + [filename])
if __name__ == '__main__':
argv = sys.argv
if len(argv) != 6:
print("Usage:",argv[0],"<content_dir> <output_dir> <theme_dir> <config_file> <lang_file>")
os._exit(1)
main(argv[1], argv[2], argv[3], argv[4], argv[5])
def combine(old, new):
if type(old) != type(new):
raise Exception("Cannot combine different types: ", type(old), type(new), old, new)
if isinstance(old, dict):
res = old.copy()
for key, value in new.items():
if key in res:
res[key] = combine(res[key], value)
else:
res[key] = value
return res
else:
return new
import datetime
from dateutil import parser as dtparser
import common
class DictConverter:
def __init__(self, factories):
self.factories = factories
def convert(self, item, lang, key = None):
if isinstance(item, dict):
res = {}
for k in item.keys():
res[k] = self.convert(item[k], lang, k)
return res
elif isinstance(item, list):
res = []
for i in item:
res.append(self.convert(i, lang))
return res
# Replace specific string keywords with their referenced objects
elif isinstance(item, str) and key == "link":
return self.factories['link'].get_by_raw(item, lang)
elif isinstance(item, str) and key == "tag":
return self.factories['tag'].get(item, lang)
elif isinstance(item, str) and key == "file":
return self.factories['file'].get(item)
elif isinstance(item, str) and key == "page":
return self.factories['page'].get(item, lang)
#elif isinstance(item, str) and key == "slug": # Doesn't work because 'slug' is used to override the slug of a page in its metadata.
# return self.factories['page'].get(item, lang)
else:
return item
class LocalizedConfigFactory(DictConverter):
def __init__(self, config, factories):
super().__init__(factories)
self.config = config
self.store = {}
def get(self, lang):
if lang not in self.store:
self.store[lang] = self.convert(self.config, lang, None)
return self.store[lang]
class FileFactory:
def __init__(self, config, factories):
self.config = config
self.factories = factories
self.store = {}
def get(self, filename):
if filename not in self.store:
self.store[filename] = File(filename, self.config, self.factories)
return self.store[filename];
def all(self):
return self.store
class File:
def __init__(self, filename, config, factories):
self.name = filename
self._config = config
self._factories = factories
self.initialized = False
def init(self, rawcontents, subpathlist):
if self.initialized:
raise Exception("Tried initializing file twice. Perhaps two files with same name exist? ", self.name)
self.initialized = True
self.rawcontents = rawcontents
self.subpathlist = subpathlist
self.link = self._factories['link'].get_by_type("file", self.name)
class PageFactory:
def __init__(self, config, factories):
self.config = config
self.factories = factories
self.store = {}
for lang in self.config['lang']['supported']:
self.store[lang] = {}
def has(self, slug, lang):
return (slug in self.store[lang])
def get(self, slug, lang):
if not self.has(slug, lang):
self.store[lang][slug] = Page(slug, lang, self.factories)
return self.store[lang][slug];
def all(self):
return self.store
class Page:
def __init__(self, slug, lang, factories):
self.slug = slug
self.lang = lang
self._config = None
self._factories = factories
self.initialized = False
self.status = "uninitialized"
self.title = "N/A"
self._metadata = {}
self.content = []
def init(self, filename, subpath, raw, metadata, content, title, category, date_created, date_modified, authors, last_modification_author, status):
self.initialized = True
self.filename = filename
self.subpath = subpath
self.raw = raw
dictconverter = DictConverter(self._factories)
self._metadata = dictconverter.convert(metadata, self.lang)
self.content = content
self.title = title
self.category = self._factories['tag'].get(category, self.lang)
self.date_created = Date(date_created, self.config)
self.date_modified = Date(date_modified, self.config)
self.status = status
# authors
self.authors = set()
for local_part, domain, name in authors:
self.authors.add(self._factories['author'].get(local_part + '@' + domain, name))
if last_modification_author:
self.last_modification_author = self._factories['author'].get(last_modification_author[0] + '@' + last_modification_author[1], last_modification_author[2])
else:
self.last_modification_author = None
#print(authors)
#print(last_modification_author)
# category
self.category.reg_page(self, True)
# url
#self.url = self.lang + '/' + self.category.name + '/' + self.slug + ".html"
self.link = self._factories['link'].get_by_type("slug", self.slug, self.lang)
gitlabsettings = self._config["gitlab"]
raw_edit_url = gitlabsettings["edit_url"]["prefix"]
raw_edit_url += '/'.join(self.subpath)
if len(self.subpath):
raw_edit_url += '/'
raw_edit_url += self.filename
raw_edit_url += gitlabsettings["edit_url"]["suffix"]
self.edit_url = self._factories['link'].get_by_raw(raw_edit_url, self.lang)
raw_view_url = gitlabsettings["view_url"]["prefix"]
raw_view_url += '/'.join(self.subpath)
if len(self.subpath):
raw_view_url += '/'
raw_view_url += self.filename
raw_view_url += gitlabsettings["view_url"]["suffix"]
self.view_url = self._factories['link'].get_by_raw(raw_view_url, self.lang)
raw_history_url = gitlabsettings["history_url"]["prefix"]
raw_history_url += '/'.join(self.subpath)
if len(self.subpath):
raw_history_url += '/'
raw_history_url += self.filename
raw_history_url += gitlabsettings["history_url"]["suffix"]
self.history_url = self._factories['link'].get_by_raw(raw_history_url, self.lang)
def get_config(self):
if not self._config:
self._config = self._factories['config'].get(self.lang)
return self._config
config = property(get_config, None, None)
def get_metadata(self):
deflang = self.config['lang']['default']
if self.lang == deflang:
return self._metadata
defpage = self._factories['page'].get(self.slug, deflang)
res = common.combine(defpage.metadata, self._metadata)
return res
metadata = property(get_metadata, None, None)
def get_tags(self):
if not self.initialized:
raise Exception("Page not initialized.")
res = []
if 'tags' in self.metadata:
rawtags = self.tags_str_to_list(self.metadata['tags'])
for t in rawtags:
if (t != self.category.name):
tag = self._factories['tag'].get(t, self.lang)
tag.reg_page(self)
res.append(tag)
res.append(self.category) # the category is also a default tag
return res
tags = property(get_tags, None, None)
def get_relevance(self):
res = {}
res['is_relevant'] = False
res['was_relevant'] = False
res['will_be_relevant'] = False
if 'relevant' in self.metadata:
lrel = self.metadata['relevant']
if not isinstance(lrel, dict):
prio = lrel
lrel = {}
lrel['prio'] = prio
if 'prio' in lrel:
res['prio']= lrel['prio']
else:
res['prio']= 0
start = Date("min", self.config)
if 'start' in lrel:
start = Date(lrel['start'], self.config)
res['start'] = start
end = Date("max", self.config)
if 'end' in lrel:
end = Date(lrel['end'], self.config)
res['end'] = end
build_date = Date("now", self.config)
if (build_date > end):
res['was_relevant'] = True
elif (build_date < start):
res['will_be_relevant'] = True
else:
res['is_relevant'] = True
return res
relevance = property(get_relevance, None, None)
def get_template(self):
if 'template' in self.metadata:
return self.metadata['template']
else:
return self.config['theme']['default_template']
template = property(get_template, None, None)
def tags_str_to_list(self, s):
if not self.initialized:
raise Exception("Page not initialized.")
res = []
l = s.split(',')
for t in l:
t = t.lower()
t = t.lstrip()
t = t.rstrip()
if len(t) > 0:
res.append(t)
return res
def get_prio(self):
if 'prio' in self.metadata:
return self.metadata['prio']
else:
return 0
prio = property(get_prio, None, None)
def __lt__(self, other): # for sorting by date modified (default)
if not self.initialized:
raise Exception("Page not initialized.")
return self.date_modified < other.date_modified
class TagFactory:
def __init__(self, config, factories):
self.config = config
self.factories = factories
self.store = {}
for lang in self.config['lang']['supported']:
self.store[lang] = {}
def get(self, name, lang):
if not isinstance(name, str):
raise Exception("name is not a string", name, type(name))
if lang == None:
raise Exception("cannot get None lang for tag", name)
if name not in self.store[lang]:
self.store[lang][name] = Tag(name, lang, self.factories)
return self.store[lang][name];
def all(self):
return self.store
class Tag:
def __init__(self, name, lang, factories):
self.name = name
self.lang = lang
self._config = None
self.pages_tuple = []
self.is_category = False
self._link = None
self._factories = factories
self._pages_cache = []
def get_config(self):
if not self._config:
self._config = self._factories['config'].get(self.lang)
return self._config
config = property(get_config, None, None)
def get_link(self):
if not self._link:
self._link = self._factories['link'].get_by_type("tag", self.name, self.lang)
return self._link
link = property(get_link, None, None)
def get_page(self):
if self._factories['page'].has('tag-' + self.name, self.lang):
return self._factories['page'].get('tag-' + self.name, self.lang)
else:
return None
page = property(get_page, None, None)
def get_color(self):
if self.page and 'color' in self.page.metadata:
return self.page.metadata['color']
else:
return self.config['theme']['default_tag_color']
color = property(get_color, None, None)
def get_title(self):
if self.page:
return self.page.title
else:
return self.name
title = property(get_title, None, None)
def reg_page(self, page, is_category = False):
if page == None:
return
if page.status != "published":
return
if page.lang != self.lang:
raise Exception("Page has different lang than Tag.", page.lang, tag.lang)
if page not in self.pages:
self.pages_tuple.append((page.prio, page))
self.pages_tuple.sort(key=lambda x: x[0])
self.pages_tuple.reverse()
if is_category:
self.is_category = is_category
def get_pages(self):
if len(self._pages_cache) != len(self.pages_tuple):
res = []
for prio, page in self.pages_tuple:
res.append(page)
self._pages_cache = res
return self._pages_cache
pages = property(get_pages, None, None)
class Date:
def __init__(self, dt, config):
if isinstance(dt, str):
if dt == "min":
dt = dtparser.parse("1970-01-01")
elif dt == "max":
dt = dtparser.parse("9999-01-01")
elif dt == "now":
dt = datetime.datetime.now()
else:
dt = dtparser.parse(dt)
if not isinstance(dt, datetime.datetime):
dt = datetime.datetime(dt.year, dt.month, dt.day)
self.dt = dt
#self.config = config
# TODO various formats
def isoformat(self):
return self.dt.isoformat()
def strftime(self, formatstr):
# https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
return self.dt.strftime(formatstr)
def __lt__(self, other):
return self.dt.timestamp() < other.dt.timestamp()
class AuthorFactory:
def __init__(self, config, factories):
self.config = config
self.factories = factories
self.store = {}
def get(self, email, name = None):
if email not in self.store:
self.store[email] = Author(email)
author = self.store[email]
author.reg_name(name)
return author;
class Author:
def __init__(self, email):
self.email = email
self.names = set()
split = email.split('@')
# TODO proper email validation
if len(split) != 2:
raise Exception("Invalid email address: ", email)
self.email_local_part = split[0]
self.email_domain = split[1]
# TODO md5 hash etc.
def reg_name(self, name):
if name == None:
return
self.names.add(name)
class LinkFactory:
def __init__(self, config, factories):
self.config = config
self.factories = factories
self.store = {}
def get_by_raw(self, rawurl, deflang):
link = Link(rawurl, self.factories, deflang) # this is dirty
lang = link.lang
if lang == None:
raise Exception("Link lang is None", lang, deflang, link.raw)
if lang not in self.store:
self.store[lang] = {}
if rawurl not in self.store[lang]:
self.store[lang][rawurl] = link
return self.store[lang][rawurl];
def get_by_type(self, reftype, refid, reflang = None):
rawurl = reftype + ':' + refid
if reflang:
rawurl += ':' + reflang
else:
reflang = self.config['lang']['default']
return self.get_by_raw(rawurl=rawurl, deflang=reflang)
class Link:
def __init__(self, rawurl, factories, deflang):
self.raw = rawurl
self._factories = factories
self.is_external = False
self.lang = deflang
self.type = None
self.alias = None
urlasplit = rawurl.split('#')
self.anchor = None
if len(urlasplit) == 2:
self.anchor = urlasplit[1]
urlwoa = urlasplit[0] # url without anchor
if len(urlwoa) == 0:
# rawurl: '#some-section'
self._url = ""
self.type = "local"
else:
components = urlwoa.split(':')
if len(components) == 1:
# rawurl: 'lageplan.uni-goettingen.de'
self._url = "https://" + urlwoa
self.is_external = True
else:
reftype = components[0]
refid = components[1]
reflang = deflang
if len(components) > 2:
reflang = components[2]
if reftype == "slug":
# rawurl: 'slug:some-page:de'
self._page = None
self._url = None
self.type = reftype
self.lang = reflang
self.refid = refid
elif reftype == "tag":
# rawurl: 'tag:some-tag:de'
self._tag = None
self._url = None
self.type = reftype
self.lang = reflang
self.refid = refid
elif reftype == "file":
# rawurl: 'file:logo.png'
self._file = None
self._url = None
self.type = reftype
self.lang = reflang
self.refid = refid
else:
# rawurl: 'https://example.com'
self._url = urlwoa
self.is_external = True
def get_page(self):
if not self._page:
self._page = self._factories['page'].get(self.refid, self.lang)
# handle link/alias pages
if 'link' in self._page.metadata:
self.alias = self._page.metadata['link']
if self.alias.type == "slug":
self.alias.get_page() # make sure that alias knows if it is an alias
return self._page
page = property(get_page, None, None)
def get_tag(self):
if not self._tag:
self._tag = self._factories['tag'].get(self.refid, self.lang)
return self._tag
tag = property(get_tag, None, None)
def get_file(self):
if not self._file:
self._file = self._factories['file'].get(self.refid)
return self._file
file = property(get_file, None, None)
def get_category(self):
if self.type == "slug":
return self.page.category
elif self.type == "tag":
return self.tag
#if self.tag.is_category:
# return self.tag
#else:
# raise Exception("category can only be called on category tags")
else:
raise Exception("category is not allowed to be called here")
category = property(get_category, None, None)
def get_path(self):
if self.type == "slug":
return '/'.join([self.page.lang, self.page.category.name, self.page.slug + '.html'])
elif self.type == "tag":
return '/'.join([self.tag.lang, 'tag', self.tag.name + '.html'])
elif self.type == "file":
return '/'.join(['file', self.file.name])
else:
raise Exception("path is not allowed to be called here", self.type)
path = property(get_path, None, None)
def get_url(self):
if self._url == None:
self._url = self.path
return self._url
url = property(get_url, None, None)
def get_urlwithanchor(self):
if self.anchor:
return self.url + "#" + self.anchor
else:
return self.url
urlwithanchor = property(get_urlwithanchor, None, None)
def get_rel(self):
"""
Get the relation in the context of the rel html attribute for this link
or None if no relation is known.
https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel
"""
if self.type != "slug":
return None
if "rel" not in self.page.metadata:
return None
return self.page.metadata["rel"]
rel = property(get_rel, None, None)
import datatypes
class Generator:
def __init__(self, config, context, factories):
self.config = config
self.context = context
self.factories = factories
def generate_context(self):
# files
self.context['files'] = self.factories['file'].all()
pages = self.factories['page'].all()
published_pages = []
hidden_pages = []
for lang in self.config['lang']['supported']:
for slug, page in pages[lang].items():
if page.status == "published":
published_pages.append(page)
elif page.status == "hidden":
hidden_pages.append(page)
# set context.pages from published_pages as a dict of languages containing dicts of slugs
all_published_pages = {}
for lang in self.config['lang']['supported']:
all_published_pages[lang] = {}
for page in published_pages:
if page.slug in all_published_pages[page.lang]:
raise Exception("duplicate language (",lang,") for slug '", slug ,"'")
all_published_pages[page.lang][page.slug] = page
self.context['pages'] = all_published_pages
# set context.hidden_pages from hidden_pages as a dict of languages containing dicts of slugs
all_hidden_pages = {}
for lang in self.config['lang']['supported']:
all_hidden_pages[lang] = {}
for page in hidden_pages:
if page.slug in all_hidden_pages[page.lang]:
raise Exception("duplicate language (",lang,") for slug '", slug ,"'")
all_hidden_pages[page.lang][page.slug] = page
self.context['hidden_pages'] = all_hidden_pages
# pages_modified
pages_modified = {}
for lang in self.config['lang']['supported']:
lang_pages = []
for page in published_pages:
if page.lang == lang:
lang_pages.append(page)
lang_pages.sort()
lang_pages.reverse()
pages_modified[lang] = lang_pages
self.context['pages_modified'] = pages_modified
# TODO draft pages
# TODO authors
# tags
self.context['tags'] = self.factories['tag'].all()
# categories
self.context['categories'] = {}
for lang in self.config['lang']['supported']:
self.context['categories'][lang] = {}
for tag in self.context['tags'][lang].values():
if tag.is_category:
self.context['categories'][lang][tag.name] = tag
# build_date
self.context['build_date'] = datatypes.Date("now", self.config)
# relevant_pages
relevant_pages_tuple = []
for page in published_pages:
if page.relevance['is_relevant']:
relevant_pages_tuple.append((page.relevance['prio'], page))
relevant_pages_tuple.sort(key=lambda x: x[0])
relevant_pages_tuple.reverse()
relevant_pages = {}
for lang in self.config['lang']['supported']:
relevant_pages[lang] = []
for prio, page in relevant_pages_tuple:
relevant_pages[page.lang].append(page)
self.context['relevant_pages'] = relevant_pages
self.context['relevant_pages_tuple'] = relevant_pages_tuple
def generate_homepage(self, writer, lang, path):
page = self.factories['config'].get(lang)['homepage']['page']
writer.write_template(page.template, path , lang, {'page': page}, page.config)
def generate_output(self, writer):
for fname, f in self.context['files'].items():
#print ("writing binary file: ", fname)
writer.write_file(f.link.path, f.rawcontents, mode="wb")
for lang in self.config['lang']['supported']:
# all published pages
print(lang, "-> Generatng all published pages.")
for page in self.context['pages'][lang].values():
writer.write_template(page.template, page.link.path, lang, {'page': page}, page.config)
# all hidden pages
print(lang, "-> Generatng all hidden pages.")
for page in self.context['hidden_pages'][lang].values():
#print(page.slug)
writer.write_template(page.template, page.link.path, lang, {'page': page}, page.config)
# all tags
print(lang, "-> Generatng all tags.")
for tag in self.context['tags'][lang].values():
writer.write_template('tag.html', tag.link.path, lang, {'tag': tag}, tag.config)
# homepages for languages
print(lang, "-> Generatng homepage.")
self.generate_homepage(writer, lang, lang + "/index.html")
# homepage
self.generate_homepage(writer, self.config['lang']['default'], "index.html")
This diff is collapsed.
This diff is collapsed.
from jinja2 import Environment, FileSystemLoader, StrictUndefined
import os
import common
class Writer:
def __init__(self, config, context, output_dir, theme_dir):
self.config = config
self.context = context
self.output_dir = output_dir
self.theme_dir = theme_dir
self.env = Environment(
loader=FileSystemLoader(theme_dir + "/templates"),
autoescape=False,
undefined = StrictUndefined
)
print("templates: ", self.env.list_templates())
def write_template(self, template, path, lang, extra_context, localized_config):
#print("write_template:", template, path, lang, extra_context, localized_config)
tmpl = self.env.get_template(template)
pathsplit = path.split('/')
#pathsplit.remove('.') # TODO remove all not just one
#pathsplit.remove('..') # TODO remove all not just one
siteurl = "."
if self.config['relative_urls']:
count = len(pathsplit) - 1
for i in range(count):
siteurl = siteurl + '/..'
else:
siteurl = self.config['siteurl']
# render template
context = {}
context = common.combine(context, self.context)
context["config"] = localized_config
context["theme"] = localized_config['theme']
context["template"] = template
context["t"] = localized_config["lang"] # translate
context["l"] = lang # current language
context["path"] = path
context["siteurl"] = siteurl
context = common.combine(context, extra_context)
#print("template: ", template)
#print("path: ", path)
#print("lang: ", lang)
#print("context: ", context)
#print("context keys: ", context.keys())
#print("tags:", type(context['tags']), context['tags'])
out = tmpl.render(context)
# write file
self.write_file(path, out)
def write_file(self, path, out, mode="w"):
# write to file
fullpath = self.output_dir + '/' + path
directory = os.path.dirname(fullpath)
if os.path.exists(fullpath):
readmode = 'r'
if 'b' in mode:
readmode = 'rb'
with open(fullpath, readmode) as f:
if out == f.read():
print("skipping writing file: ", fullpath)
return
print("writing file: ", fullpath)
#print("dir: ", directory)
os.makedirs(directory, exist_ok=True)
with open(fullpath, mode) as f:
f.write(out)
<style>
ul {
margin:auto;
margin-top: 6rem;
padding: 2rem;
background-color: #dff0d8;
color: #3c763d;
border: 0.1rem solid #d6e9c6;
border-radius: 0.5rem;
width: 20rem;
}
a {
color: #337ab7;
padding: 0.5rem;
}
li {
padding: 0.5rem;
}
</style>
<ul>
<li><a href="./docs-output">Dokumentation</a></li>
<li><a href="./output">Preview Seite</a></li>
</ul>
This diff is collapsed.
Subproject commit 600692ad9d3552cc25f85510d5797bc942ecc9f7
pandoc-2.18
\ No newline at end of file
resources/assets/banner-logo.png

14 KiB

This diff is collapsed.
<!DOCTYPE html>
<html lang="de">
<head>
<title>{{title}}</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- <meta HTTP-EQUIV="REFRESH" content="50; url=#"> -->
<!-- <link rel="stylesheet" type="text/css" href="/css/style.css"> -->
<link rel="stylesheet" type="text/css" href="/static/style.css">
</head>
<body>
<aside><a href="/index.html"><img src="/resources/assets/banner-logo.png" alt="Logo der Fachgruppe" /></a></aside>
<header>
<nav>
<a href="/Über-uns.html">Über uns</a>
<a href="/Fachgruppentreffen.html">Fachgruppentreffen</a>
<a href="/Cookie-Talks.html">Cookie Talks</a>
<a href="/studentische-Informatiktage-(sIT).html">studentische Informatiktage (sIT)</a>
</nav>
</header>
<!--<nav id="toc">
{{toc}}
</nav>-->
<main>
<header>
<h1>{{title}}</h1>
</header>
{{content}}
<footer>
<span>
Last edited by <strong>{{lasteditedauthorname}}</strong>, {{lastediteddaterelative}} ({{lastediteddate}}). <a href="{{editurl}}" target="_blank">[EDIT]</a>
</span>
</footer>
</main>
<!--<aside>
aside ist hier glaube ich nicht gut
</aside>-->
<footer>
blablabla datenschutz,impressum etc...
</footer>
</body>
</html>
.PHONY: run
run:
make -C ../fgs
This diff is collapsed.
This diff is collapsed.
{% extends "base.html" %}
{% block title %}{{ l[lang].title_prefix }}{{ l[lang].sitename }}{{ l[lang].title_suffix }} - {{ category }}{%endblock%}
{% block content %}
<section>
{{ render.section({"type": "category", "category": category.slug, "num": None}) }}
</section>
{% endblock content %}