+def validate_motion_access(privilege):
+ def decorator(f):
+ def decorated_function(motion):
+ db = get_db()
+ with db.xact():
+ rv = db.prepare("SELECT id, type, deadline < CURRENT_TIMESTAMP AS expired, canceled FROM motion WHERE identifier=$1 AND host=$2")(motion, request.host);
+ if len(rv) == 0:
+ return _('Error, Not found'), 404
+ id = rv[0].get("id")
+ if not may(privilege, rv[0].get("type")):
+ return _('Forbidden'), 403
+ if rv[0].get("canceled") is not None:
+ return _('Error, motion was canceled'), 403
+ if rv[0].get("expired"):
+ return _('Error, out of time'), 403
+ return f(motion, id)
+ decorated_function.__name__ = f.__name__
+ return decorated_function
+ return decorator
+
+def validate_motion_access_vote(privilege):
+ simple_decorator = validate_motion_access(privilege)
+ def decorator(f):
+ def decorated_function(motion, voter):
+ return simple_decorator(lambda motion, id : f(motion, voter, id))(motion)
+ decorated_function.__name__ = f.__name__
+ return decorated_function
+ return decorator
+
+@app.route("/motion/<string:motion>/cancel", methods=['POST'])
+@validate_motion_access('cancel')
+def cancel_motion(motion, id):
+ if request.form.get("reason", "none") == "none":
+ return _('Error, form requires reason'), 500
+ rv = get_db().prepare("UPDATE motion SET canceled=CURRENT_TIMESTAMP, cancelation_reason=$1, canceled_by=$2 WHERE identifier=$3 AND host=$4 AND canceled is NULL")(request.form.get("reason", ""), g.voter, motion, request.host)
+ return motion_edited(motion)
+
+@app.route("/motion/<string:motion>/finish", methods=['POST'])
+@validate_motion_access('finish')
+def finish_motion(motion, id):
+ rv = get_db().prepare("UPDATE motion SET deadline=CURRENT_TIMESTAMP WHERE identifier=$1 AND host=$2 AND canceled is NULL")(motion, request.host)
+ return motion_edited(motion)
+
+@app.route("/motion/<string:motion>")
+def show_motion(motion):
+ p = get_db().prepare("SELECT motion.*, poser.email AS poser, canceler.email AS canceler, (motion.deadline > CURRENT_TIMESTAMP AND canceled is NULL) AS running, vote.result FROM motion "\
+ + "LEFT JOIN vote on vote.motion_id=motion.id AND vote.voter_id=$2 "\
+ + "LEFT JOIN voter poser ON poser.id = motion.posed_by "\
+ + "LEFT JOIN voter canceler ON canceler.id = motion.canceled_by "
+ + "WHERE motion.identifier=$1 AND motion.host=$3")
+ resultmotion = p(motion, g.voter, request.host)
+ if len(resultmotion) == 0:
+ return _('Error, Not found'), 404
+
+ p = get_db().prepare("SELECT voter.email FROM vote INNER JOIN voter ON vote.proxy_id = voter.id WHERE vote.motion_id=$1 AND vote.voter_id=$2 AND vote.proxy_id <> vote.voter_id")
+ resultproxyname = p(resultmotion[0][0], g.voter)
+
+ p = get_db().prepare("SELECT v.result, proxy.voter_id, voter.email, CASE WHEN proxy.proxy_id = v.proxy_id THEN NULL ELSE voter.email END AS owneremail FROM proxy LEFT JOIN "\
+ + "(SELECT vote.voter_id, vote.result, vote.proxy_id FROM vote "\
+ + "WHERE vote.motion_id=$1) AS v ON proxy.voter_id = v.voter_id "\
+ + "LEFT JOIN voter ON proxy.voter_id = voter.id "\
+ + "WHERE proxy.proxy_id=$2 AND proxy.revoked IS NULL")
+ resultproxyvote = p(resultmotion[0][0], g.voter)
+
+ votes = None
+ if may("audit", resultmotion[0].get("type")) and not resultmotion[0].get("running") and not resultmotion[0].get("canceled"):
+ votes = get_db().prepare("SELECT vote.result, voter.email FROM vote INNER JOIN voter ON voter.id = vote.voter_id WHERE vote.motion_id=$1")(resultmotion[0].get("id"));
+ votes = get_db().prepare("SELECT vote.result, voter.email, CASE voter.email WHEN proxy.email THEN NULL ELSE proxy.email END as proxyemail FROM vote INNER JOIN voter ON voter.id = vote.voter_id INNER JOIN voter as proxy ON proxy.id = vote.proxy_id WHERE vote.motion_id=$1")(resultmotion[0].get("id"));
+ return render_template('single_motion.html', motion=resultmotion[0], may_vote=may("vote", resultmotion[0].get("type")), may_cancel=may("cancel", resultmotion[0].get("type")), votes=votes, proxyvote=resultproxyvote, proxyname=resultproxyname, languages=get_languages())
+
+@app.route("/motion/<string:motion>/vote/<string:voter>", methods=['POST'])
+@validate_motion_access_vote('vote')
+def vote(motion, voter, id):