diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 1989e049c4c3c37f7c9ae3f892294716633f36e5..5147222f6ae8c3e6c757fee6c486358c6c9d850f 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -2,7 +2,7 @@ ## Setup -* Install python3 and virtualenv +* Install python3, virtualenv, redis and a database program (postgres, mysql, sqlite does not work with alembic migrations) * Create a virtualenv and install the requirements ```sh @@ -22,7 +22,7 @@ cp config.py.example config.py - Don't forget to enter your database connection - And your authentication backend - And deactivate everything you do not use - +* create the path where PDFs will be stored (`DOCUMENTS_PATH` in config.py) * Fill your database ```sh diff --git a/config.py.example b/config.py.example index ee7824736c7246d08ac2f3248fa6fbbe917d3c30..1aa605cbd8a2665a12850e18ec76aeb5ed66bea1 100644 --- a/config.py.example +++ b/config.py.example @@ -77,9 +77,10 @@ AUTH_BACKENDS = [ StaticUserManager( users=( ("username", "password", ("group1", "group2")), - ("testuser", "abc123", ("group1")), + ("testuser", "abc123", ("group1",)), ) - ) + ), + PAMManager(), ] OBSOLETION_WARNING = """Please migrate your account!""" # not important @@ -147,6 +148,7 @@ FONTS = { } # local filesystem path to save compiled and uploaded protocols (and attachments) +# create this path! DOCUMENTS_PATH = "documents" # keywords indicating private protocol parts diff --git a/models/database.py b/models/database.py index a559fd02e3302651ab9f296ed3bf2ee4d4fed64c..49b0635b028a8875a8cc798c3a9422ece1eae284 100644 --- a/models/database.py +++ b/models/database.py @@ -611,7 +611,7 @@ class Todo(DatabaseModel): return " ".join(parts) def render_latex(self, current_protocol=None): - return r"\textbf{{{}}}: {}: {} -- {}".format( + return r"\Todo{{{}}}{{{}}}{{{}}}{{{}}}".format( "Neuer Todo" if self.is_new(current_protocol) else "Todo", escape_tex(self.who), escape_tex(self.description), diff --git a/protoparser.py b/protoparser.py index cac3b434d0839225791a635d45b6ad6f93584c86..bee63917caf77d0178d08d90adef684569bcaad7 100644 --- a/protoparser.py +++ b/protoparser.py @@ -221,12 +221,10 @@ class Tag: return "" return self.todo.render_latex(current_protocol=protocol) elif self.name == "beschluss": - parts = [r"\textbf{{Beschluss:}} {}".format(self.decision.content)] if len(self.decision.categories): - parts.append( - r"\textit{{({})}}".format(self.decision.get_categories_str()) - ) - return " ".join(parts) + return r"\Beschluss[{}]{{{}}}".format(self.decision.get_categories_str(),self.decision.content) + else: + return r"\Beschluss{{{}}}".format(self.decision.content) elif self.name == "footnote": return r"\footnote{{{}}}".format(self.values[0]) return r"\textbf{{{}:}} {}".format(escape_tex(self.name.capitalize()), escape_tex(";".join(self.values))) @@ -433,7 +431,10 @@ class Fork(Element): else: return "\n".join([escape_tex(name_line), begin_line, content_lines, end_line]) elif render_type == RenderType.wikitext or render_type == RenderType.dokuwiki: - title_line = "{0} {1} {0}".format("=" * (level + 2), name_line) + equal_signs = level + 2 + if render_type == RenderType.dokuwiki: + equal_signs = 6 - level + title_line = "{0} {1} {0}".format("=" * equal_signs, name_line) content_parts = [] for child in self.children: part = child.render(render_type, show_private, level=level+1, protocol=protocol) diff --git a/server.py b/server.py index 61eb7e3853dd079bdb09e9e9795bc8860d15f4f6..eec035d5e969bf3d3a5b2d3d1633dd37ae530cdc 100755 --- a/server.py +++ b/server.py @@ -18,6 +18,7 @@ import os from datetime import datetime import math import mimetypes +import subprocess import config from shared import db, date_filter, datetime_filter, date_filter_long, date_filter_short, time_filter, time_filter_short, user_manager, security_manager, current_user, check_login, login_required, group_required, class_filter, needs_date_test, todostate_name_filter, code_filter, indent_tab_filter @@ -80,6 +81,18 @@ app.jinja_env.globals.update(max=max) app.jinja_env.globals.update(dir=dir) app.jinja_env.globals.update(now=datetime.now) +def get_git_revision(): + gitlab_url = "https://git.fsmpi.rwth-aachen.de/protokollsystem/proto3" + commit_hash = subprocess.check_output(["git", "log", "-g", "-1", "--pretty=%H"]).decode("UTF-8").strip() + timestamp = int(subprocess.check_output(["git", "log", "-g", "-1", "--pretty=%at"]).strip()) + commit_date = datetime.fromtimestamp(timestamp) + return {"url": gitlab_url, "hash": commit_hash, "date": commit_date} + +try: + app.jinja_env.globals["git_revision"] = get_git_revision() +except: + pass + # blueprints here @manager.command diff --git a/tasks.py b/tasks.py index b5c4818af2e6dcfc72cb4b93b0fa3a5b4e5c0e1f..28a97703a14de4c61278a4d4dddc595dde1d72bf 100644 --- a/tasks.py +++ b/tasks.py @@ -517,14 +517,22 @@ def push_to_dokuwiki(protocol, content, summary): @celery.task def push_to_dokuwiki_async(protocol_id, content, summary): - protocol = Protocol.query.filter_by(id=protocol_id).first() - with xmlrpc.client.ServerProxy(config.WIKI_API_URL) as proxy: - if not proxy.wiki.putPage(protocol.get_wiki_title(), - content, {"sum": "Automatisch generiert vom Protokollsystem 3."}): - error = protocol.create_error("Pushing to Wiki", - "Pushing to Wiki failed." "") - db.session.add(error) - db.session.commit() + with app.app_context(): + protocol = Protocol.query.filter_by(id=protocol_id).first() + with xmlrpc.client.ServerProxy(config.WIKI_API_URL) as proxy: + try: + if not proxy.wiki.putPage(protocol.get_wiki_title(), + content, {"sum": "Automatisch generiert vom Protokollsystem 3."}): + error = protocol.create_error("Pushing to Wiki", + "Pushing to Wiki failed." "") + db.session.add(error) + db.session.commit() + except xmlrpc.client.Error as exception: + error = protocol.create_error("Pushing to Wiki", + "XML RPC Exception", + str(exception)) + db.session.add(error) + db.session.commit() def compile(content, protocol, show_private, maxdepth): compile_async.delay(content, protocol.id, show_private=show_private, maxdepth=maxdepth) @@ -595,19 +603,20 @@ def compile_async(content, protocol_id, show_private=False, use_decision=False, except subprocess.SubprocessError: log = "" total_log_filename = os.path.join(compile_dir, log_filename) - if os.path.isfile(total_log_filename): - with open(total_log_filename, "r") as log_file: - log = "Log:\n\n" + add_line_numbers(log_file.read()) - else: - log = "Logfile not found." total_source_filename = os.path.join(compile_dir, protocol_source_filename) + log = "" 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()) + log += "Source:\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()) + if os.path.isfile(total_log_filename): + with open(total_log_filename, "r") as log_file: + log += "\n\nLog:\n\n" + add_line_numbers(log_file.read()) + else: + log += "\n\nLogfile not found." error = protocol.create_error("Compiling", "Compiling LaTeX failed", log) db.session.add(error) db.session.commit() diff --git a/templates/documentation.html b/templates/documentation.html index 5c6758b5a55867938b6fb45e411f84e2ae5668b8..fd539659aa1c8805ff88fe6d9cd52dd0aadb4c60 100644 --- a/templates/documentation.html +++ b/templates/documentation.html @@ -214,6 +214,11 @@ Optional kann zusätzlich eine Uhrzeit angegeben werden: <code>[sitzung;Datum;Uhrzeit]</code> (z.B. <code>[sitzung;01.01.2018;9:00]</code>). {% endif %} + {% if git_revision %} + <h3 id="version">Version</h4> + Dieses Protokollsystem nutzt die Software <a href="{{git_revision.url}}">„Protokollsystem 3“</a> in der Version vom <a href="{{git_revision.url}}/commit/{{git_revision.hash}}">{{git_revision.date|datify}}</a>. + Alle Änderungen, die seitdem hinzugekommen sind, kannst du <a href="{{git_revision.url}}/compare/{{git_revision.hash}}...master">hier</a> sehen. + {% endif %} </div> </div> {% endblock %} diff --git a/templates/protocol-show.html b/templates/protocol-show.html index 2faf8783bc6b73ffe9c7a11a2941339a46543057..882700c1e0816c0925c015ef2bdfbf02f72f5d5d 100644 --- a/templates/protocol-show.html +++ b/templates/protocol-show.html @@ -26,7 +26,7 @@ {% endif %} {% if not protocol.public %} {% if config.ETHERPAD_ACTIVE %} - <a class="btn btn-primary" href="{{url_for("etherpush_protocol", protocol_id=protocol.id)}}"{% if large_time_diff %} onclick="return confirm('Bist du dir sicher, dass du das Template bereits in das Etherpad kopieren willst? Die Sitzung ist erst in {{time_diff.days}} Tagen.');"{% endif %}>Etherpad</a> + <a class="btn btn-primary" href="{{url_for("etherpush_protocol", protocol_id=protocol.id)}}"{% if large_time_diff %} onclick="return confirm('Bist du dir sicher, dass du das Template bereits in das Etherpad kopieren willst? Die Sitzung ist erst in {{time_diff.days}} Tagen.');"{% endif %} target="_blank">Etherpad</a> {% endif %} {% endif %} {% if not protocol.is_done() %} diff --git a/templates/protocol.dokuwiki b/templates/protocol.dokuwiki index 24752de15ce2a9ba52a5af4dc9c4a2b4e810ab5c..21574b506ff85924d9ba46ec844cd59d10cba3ac 100644 --- a/templates/protocol.dokuwiki +++ b/templates/protocol.dokuwiki @@ -1,10 +1,10 @@ -== Beschlüsse == +====== Beschlüsse ====== <env> if protocol.decisions|length > 0 </env> <env> for decision in protocol.decisions </env> -* <var>decision.content</var> + * <var>decision.content</var> <env> endfor </env> <env> else </env> -* keine Beschlüsse + * keine Beschlüsse <env> endif </env> <env> for top in tree.children </env> diff --git a/templates/protocol.tex b/templates/protocol.tex index 206a7510a89280ee30f69a34b4929248acd61487..bc289ba5688c0428d70728a076a696911868b06f 100644 --- a/templates/protocol.tex +++ b/templates/protocol.tex @@ -5,7 +5,9 @@ \usepackage{pdfpages} \usepackage{eurosym} %\usepackage[utf8]{inputenc} -\usepackage[pdfborder={0 0 0}]{hyperref} +\usepackage[hyphens]{url} +\usepackage[pdfborder={0 0 0},breaklinks=true]{hyperref} +\def\UrlBreaks{\do\/\do-\do\&\do.\do,\do;\do\_\do?\do\#} %\usepackage{ngerman} % \usepackage[left]{lineno} %\usepackage{footnote} diff --git a/templates/protokoll2.cls b/templates/protokoll2.cls index b181f14dd0dbc3b1384f852151bcfad58782a947..75f01685b00022031bcf6a557383ef85b58a0a8b 100644 --- a/templates/protokoll2.cls +++ b/templates/protokoll2.cls @@ -1,7 +1,7 @@ % protokoll.cls -- version 0.1 -- last changed: see mtime of the file ;) % % -% Author: Sebastian G�nther <samson@asta.rwth-aachen.de> +% Author: Sebastian Günther <samson@asta.rwth-aachen.de> % zuerst mal LaTeX's formalzeug \NeedsTeXFormat{LaTeX2e} @@ -126,7 +126,7 @@ \renewcommand{\thesubsection}{TOP~\arabic{section}~(\alph{subsection})} \newcommand{\TOP}[1]{\section{#1}} \newcommand{\unterTOP}[1]{\subsection{#1}} -% F�rs Inhaltsverzeichnisanpassen +% Fürs Inhaltsverzeichnisanpassen \renewcommand{\l@section}{\@dottedtocline{1}{1.5em}{4em}} \renewcommand{\l@subsection}{\@dottedtocline{2}{5.5em}{5.2em}} \renewcommand{\contentsname}{Tagesordnung} @@ -166,7 +166,7 @@ }% {\end{list}} -% Abk�rzungen +% Abkürzungen \newenvironment{Abk}{% \begin{list}{}{% Abkürzungen: @@ -183,7 +183,7 @@ }% {\end{list}} -% Unterschriften f�r eine und zwei Personen +% Unterschriften für eine und zwei Personen \newcommand{\Unterschrift}[2]{ \vspace*{1.4cm} \begin{center} @@ -250,3 +250,6 @@ \newcommand{\PE}[2]{\textbf{Persönliche Erklärung von #1:}\\\emph{\glqq{}#2\grqq{}}\\} +% Styling der Todo und Beschlusstags im Protokoll +\newcommand{\Todo}[4]{\textbf{{#1}}: #2: #3 -- #4} +\newcommand{\Beschluss}[2][\empty]{\textbf{Beschluss:} #2 \def\temp{#1}\ifx\temp\empty\else\textit{(#1)}\fi} diff --git a/views/forms.py b/views/forms.py index fe3a2affd485bf0e7d3c6f2a83d2d655e3946bd5..276a8bfbd35920df774d19a51537943c6401fdfa 100644 --- a/views/forms.py +++ b/views/forms.py @@ -99,8 +99,13 @@ class IPNetworkField(Field): raise ValueError(self.gettext("Not a valid IP Network: {}".format(str(exc)))) self.data = ",".join(map(str, result_parts)) +class FocusedStringField(StringField): + def __call__(self, **kwargs): + kwargs['autofocus'] = True + return super().__call__(**kwargs) + class LoginForm(FlaskForm): - username = StringField("Benutzer", validators=[InputRequired("Bitte gib deinen Benutzernamen ein.")]) + username = FocusedStringField("Benutzer", validators=[InputRequired("Bitte gib deinen Benutzernamen ein.")]) password = PasswordField("Passwort", validators=[InputRequired("Bitte gib dein Passwort ein.")]) permanent = BooleanField("Eingeloggt bleiben?")