diff --git a/.gitignore b/.gitignore index 49984f1f98db5cb0c0b5d00f30a2500374ea313f..858b1e1da5fd77e0e517d662c54481c20a8c7d0e 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ documents/ test/ *.sql pg_to_sqlite.sh +local-templates/ diff --git a/config.py.example b/config.py.example index fbb680399656338b2a03321fe8320bfe19f2f73f..94d34a12cf1f761c88fd2fce9f7e4e2af53a599e 100644 --- a/config.py.example +++ b/config.py.example @@ -146,3 +146,17 @@ LATEX_BULLETPOINTS = [ r"\textasteriskcentered", r"\textperiodcentered" ] + +# optional: path to additional jinja-templates +#LATEX_LOCAL_TEMPLATES = "local-templates" +# optional: the template to include at the top of protocol.tex +#LATEX_LOGO_TEMPLATE = "asta-logo.tex" +# optional: custom protocol page geometry +#LATEX_GEOMETRY = "bottom=1.6cm,top=1.6cm,inner=2.5cm,outer=1.0cm,footskip=1.0cm,headsep=0.6cm" +# optional: custom protocol pagestyle +#LATEX_PAGESTYLE = "fancy" +# optional: additional latex packages +#LATEX_ADDITIONAL_PACKAGES = ["[absolute]{textpos}", "{fancyheadings}"] +# optional: include header and footer in asta-style, not just a page number on top +#LATEX_HEADER_FOOTER = True + diff --git a/parser.py b/parser.py index 9be2c3e8b82ce8dc8d25a7ca4d460346cfdf5abf..6ac375715396105177bd03a21b08e6ee8383d2ad 100644 --- a/parser.py +++ b/parser.py @@ -32,6 +32,7 @@ class RenderType(Enum): latex = 0 wikitext = 1 plaintext = 2 + html = 3 def _not_implemented(self, render_type): return NotImplementedError("The rendertype {} has not been implemented for {}.".format(render_type.name, self.__class__.__name__)) @@ -175,6 +176,8 @@ class Text: return self.text elif render_type == RenderType.plaintext: return self.text + elif render_type == RenderType.html: + return self.text else: raise _not_implemented(self, render_type) @@ -235,6 +238,24 @@ class Tag: return "" return self.todo.render_wikitext(current_protocol=protocol) return "'''{}:''' {}".format(self.name.capitalize(), ";".join(self.values)) + elif render_type == RenderType.html: + if self.name == "url": + return "<a href=\"{0}\">{0}</a>".format(self.values[0]) + elif self.name == "todo": + if not show_private: + return "" + if getattr(self, "todo", None) is not None: + return self.todo.render_html(current_protocol=protocol) + else: + return "<b>Todo:</b> {}".format(";".join(self.values)) + elif self.name == "beschluss": + if getattr(self, "decision", None) is not None: + parts = ["<b>Beschluss:</b>", self.decision.content] + if self.decision.category is not None: + parts.append("<i>{}</i>".format(self.decision.category.name)) + return " ".join(parts) + else: + return "<b>Beschluss:</b> {}".format(self.values[0]) else: raise _not_implemented(self, render_type) @@ -295,6 +316,11 @@ class Remark(Element): return "{}: {}".format(self.name, self.value) elif render_type == RenderType.plaintext: return "{}: {}".format(RenderType.plaintex) + elif render_type == RenderType.html: + return "<p>{}: {}</p>".format(self.name, self.value) + else: + raise _not_implemented(self, render_type) + def dump(self, level=None): if level is None: @@ -399,6 +425,19 @@ class Fork(Element): return "" else: return content_lines + elif render_type == RenderType.html: + title_line = "<h{depth}>{content}</h{depth}>".format(depth=level+1, content=name_line) + content_parts = [] + for child in self.children: + part = child.render(render_type, show_private, level=level+1, protocol=protocol) + if len(part.strip()) == 0: + continue + content_parts.append("<p>{}</p>".format(part)) + content_lines = "{}\n\n{}".format(title_line, "\n".join(content_parts)) + if self.test_private(self.name) and not show_private: + return "" + else: + return content_lines else: raise _not_implemented(self, render_type) diff --git a/server.py b/server.py index eeb3fdb8ee0d6796cf4e23d57631002f56b8778e..de2fbe8ae40c9e3cfff567ac184b2a0d128a5603 100755 --- a/server.py +++ b/server.py @@ -63,6 +63,12 @@ app.jinja_env.filters["fancy_join"] = fancy_join app.jinja_env.tests["auth_valid"] = security_manager.check_user app.jinja_env.tests["needs_date"] = needs_date_test +additional_templates = getattr(config, "LATEX_LOCAL_TEMPLATES", None) +if additional_templates is not None and os.path.isdir(additional_templates): + if additional_templates not in app.jinja_loader.searchpath: + app.jinja_loader.searchpath.append(additional_templates) + + import tasks app.jinja_env.globals.update(check_login=check_login) diff --git a/tasks.py b/tasks.py index 269483289b8a9ebbe9da4795c99f5c92455936c9..d1eff0d51a5f66106919392f340b410f278d5a43 100644 --- a/tasks.py +++ b/tasks.py @@ -38,6 +38,26 @@ texenv.filters["datify_short"] = date_filter_short texenv.filters["datetimify"] = datetime_filter texenv.filters["timify"] = time_filter texenv.filters["class"] = class_filter +logo_template = getattr(config, "LATEX_LOGO_TEMPLATE", None) +if logo_template is not None: + texenv.globals["logo_template"] = logo_template +latex_geometry = getattr(config, "LATEX_GEOMETRY", "vmargin=1.5cm,hmargin={1.5cm,1.2cm},bindingoffset=8mm") +texenv.globals["latex_geometry"] = latex_geometry +raw_additional_packages = getattr(config, "LATEX_ADDITIONAL_PACKAGES", None) +additional_packages = [] +if raw_additional_packages is not None: + for package in raw_additional_packages: + if "{" not in package: + package = "{{{}}}".format(package) + additional_packages.append(package) +print(raw_additional_packages) +print(additional_packages) +texenv.globals["additional_packages"] = additional_packages +latex_pagestyle = getattr(config, "LATEX_PAGESTYLE", None) +if latex_pagestyle is not None: + texenv.globals["latex_pagestyle"] = latex_pagestyle +latex_header_footer = getattr(config, "LATEX_HEADER_FOOTER", False) +texenv.globals["latex_header_footer"] = latex_header_footer mailenv = app.create_jinja_environment() mailenv.trim_blocks = True @@ -403,11 +423,12 @@ def compile_async(content, protocol_id, show_private=False, use_decision=False, current = os.getcwd() protocol_source_filename = "protocol.tex" protocol_target_filename = "protocol.pdf" + protocol_class_filename = "protokoll2.cls" log_filename = "protocol.log" with open(os.path.join(compile_dir, protocol_source_filename), "w") as source_file: source_file.write(content) - protocol2_class_source = texenv.get_template("protokoll2.cls").render(fonts=config.FONTS, maxdepth=maxdepth, bulletpoints=config.LATEX_BULLETPOINTS) - with open(os.path.join(compile_dir, "protokoll2.cls"), "w") as protocol2_class_file: + protocol2_class_source = texenv.get_template(protocol_class_filename).render(fonts=config.FONTS, maxdepth=maxdepth, bulletpoints=config.LATEX_BULLETPOINTS) + with open(os.path.join(compile_dir, protocol_class_filename), "w") as protocol2_class_file: protocol2_class_file.write(protocol2_class_source) os.chdir(compile_dir) command = [ @@ -460,6 +481,10 @@ def compile_async(content, protocol_id, show_private=False, use_decision=False, if os.path.isfile(total_source_filename): with open(total_source_filename, "r") as source_file: log += "\n\nSource:\n\n" + add_line_numbers(source_file.read()) + total_class_filename = os.path.join(compile_dir, protocol_class_filename) + if os.path.isfile(total_class_filename): + with open(total_class_filename, "r") as class_file: + log += "\n\nClass:\n\n" + add_line_numbers(class_file.read()) error = protocol.create_error("Compiling", "Compiling LaTeX failed", log) db.session.add(error) db.session.commit() diff --git a/templates/decision.tex b/templates/decision.tex index 323f818fecec9e2b433aebbee808a87aafb600d0..3c7b9956eb35ed170ad32be5e85be599aa1ae5ff 100644 --- a/templates/decision.tex +++ b/templates/decision.tex @@ -18,6 +18,9 @@ \begin{document} %\thispagestyle{plain} %ggf kommentarzeichen entfernen +\ENV{if logo_template is defined} + \ENV{include logo_template} +\ENV{endif} \Titel{ \large Protokoll: \VAR{protocol.protocoltype.name|escape_tex} \\\normalsize \VAR{protocol.protocoltype.organization|escape_tex} diff --git a/templates/protocol.tex b/templates/protocol.tex index 41a759ec41cbccf499b1dda103cb415d1773f6fc..f29e5a2eee34f739ad40e7119963c1609f28a02b 100644 --- a/templates/protocol.tex +++ b/templates/protocol.tex @@ -18,6 +18,9 @@ \begin{document} %\thispagestyle{plain} %ggf kommentarzeichen entfernen +\ENV{if logo_template is defined} + \ENV{include logo_template} +\ENV{endif} \Titel{ \large \ENV{if show_private}Internes \ENV{endif} Protokoll: \VAR{protocol.protocoltype.name|escape_tex} \\\normalsize \VAR{protocol.protocoltype.organization|escape_tex} diff --git a/templates/protokoll2.cls b/templates/protokoll2.cls index a1b5467828373bb7cdc44806c50b507fb49fa55d..c732b57f0bf95c5ddb4059c5fba79b30b267dfb7 100644 --- a/templates/protokoll2.cls +++ b/templates/protokoll2.cls @@ -15,7 +15,7 @@ % so ein paar sachen die wir eh immer brauchen \RequirePackage[ngerman]{babel} -\RequirePackage[vmargin=1.5cm,hmargin={1.5cm,1.2cm},bindingoffset=8mm]{geometry} +\RequirePackage[\VAR{latex_geometry}]{geometry} %\RequirePackage{lineno} \RequirePackage{longtable} \RequirePackage{framed} @@ -27,6 +27,9 @@ \RequirePackage{xcolor} \RequirePackage[breakable]{tcolorbox} \RequirePackage{linegoal} +\ENV{for package in additional_packages} + \RequirePackage\VAR{package} +\ENV{endfor} \setlistdepth{\VAR{maxdepth}} \renewlist{itemize}{itemize}{\VAR{maxdepth}} @@ -88,7 +91,19 @@ % nicht einrücken und benutzerinnendefinierte kopfzeile \setlength{\parindent}{0cm} \setlength{\parskip}{1ex} -\pagestyle{myheadings} +\pagestyle{\VAR{latex_pagestyle|default("myheadings")}} + +\ENV{if latex_header_footer} + \renewcommand{\headrulewidth}{0pt} + \renewcommand{\footrulewidth}{0.5pt} + \def\footerprotocoltext{\relax} + \rfoot[]{\rmfamily\small\footerprotocoltext~~|~~\textbf{\thepage}} + \lfoot[\rmfamily\small\textbf{\thepage}~~|~~\footerprotocoltext]{} + \cfoot{} + \chead{} + \lhead[]{} + \rhead[]{} +\ENV{endif} %\renewcommand*{\rmdefault}{phv} %\renewcommand*{\sfdefault}{phv} @@ -96,9 +111,10 @@ % Titel \newcommand{\Kopf}[1]{\markboth{#1}{#1}} \newcommand{\Titel}[2]{ - \markboth{#2}{#2} - \markright{#2} - \thispagestyle{plain} + \def\footerprotocoltext{#2} + %\markboth{#2}{#2} + %\markright{#2} + %\thispagestyle{plain} \begin{center} \textbf{#1} \end{center}