]> WPIA git - motion.git/commitdiff
Merge branch 'finish_move' into 'master' master
authorFelix Dörre <felix@dogcraft.de>
Tue, 20 Apr 2021 19:09:14 +0000 (21:09 +0200)
committerFelix Dörre <felix@dogcraft.de>
Tue, 20 Apr 2021 19:09:14 +0000 (21:09 +0200)
upd: move cancel and finish button and add confirm question

See merge request felixdoerre/motion!34

requirements.txt
sql/schema.sql
static/js/expert.js [new file with mode: 0644]
templates/base.html
templates/single_motion.html
tests/test_motion.py

index 17abe88225e72aed9be46d795a7647ed2b281c31..315db312ae1376035e5d8146c377a1e1c852cbc4 100644 (file)
@@ -3,7 +3,7 @@ Flask==1.1.2
 Flask-Babel==1.0.0
 itsdangerous==0.24
 Jinja2==2.11.2
-MarkupSafe==1.0
+MarkupSafe==1.1.1
 py-postgresql==1.2.1
 Werkzeug==1.0.1
 Flask-Markdown==0.3
index c3967a45391b9230a60f436d8508e064ef7ebdfc..56055bc15bb434a91f88f21886b30c2918911765 100644 (file)
@@ -22,6 +22,7 @@ CREATE TABLE motion (id serial NOT NULL,
 CREATE UNIQUE INDEX motion_ident ON motion (identifier);
 
 DROP TABLE IF EXISTS vote;
+DROP TABLE IF EXISTS voteresult;
 DROP TYPE IF EXISTS "vote_type";
 CREATE TYPE "vote_type" AS ENUM ('yes', 'no', 'abstain');
 CREATE TABLE vote (motion_id INTEGER NOT NULL,
diff --git a/static/js/expert.js b/static/js/expert.js
new file mode 100644 (file)
index 0000000..a4add1a
--- /dev/null
@@ -0,0 +1,183 @@
+(function() {
+       var modal = undefined;
+       
+       function showModal(content){
+               var HTML = `<div class="modal fade" id="confirmation-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
+  <div class="modal-dialog" role="document">
+    <div class="modal-content">
+      <div class="modal-header">
+        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&#x1F5D9;</span></button>
+        <h4 class="modal-title" id="myModalLabel">&nbsp;</h4>
+      </div>
+      <div class="modal-body">
+        Body
+      </div>
+      <div class="modal-footer">
+        <button type="button" class="btn btn-default btn-cancel" data-dismiss="modal">&#x1F5D9;</button>
+        <button type="button" class="btn btn-danger btn-confirm">&checkmark;</button>
+      </div>
+    </div>
+  </div>
+</div>`;
+               if(modal === undefined) {
+                       modal = $(HTML);
+                       modal.action = function(){};
+                       modal.appendTo("body");
+                       $(modal.get(0)).find(".modal-footer .btn-confirm").click(function(){
+                               modal.action();
+                               modal.modal("hide");
+                       });
+               }
+               var m = $(modal.get(0));
+               m.find(".modal-body").text($(content).attr("data-confirm"));
+               var reply = $(content).attr("data-reply").split(",");
+               m.find(".modal-footer .btn-cancel").text(reply[0]);
+               m.find(".modal-footer .btn-confirm").text(reply[1]);
+               modal.action=function(){
+                       content.confirmed=true;
+                       $(content).click();
+               };
+               modal.modal("show");
+       }
+       function initConfirm() {
+               $(".btn-confirm").click(function(){
+                       if(this.confirmed === true){
+                               return true;
+                       }
+                       showModal(this);
+                       return false;
+               });
+       }
+       function showExpert(isExpert)
+       {
+         var elements = document.getElementsByClassName("expert");
+         for(var i = 0; elements.length > i; i++)
+         {
+           if(!isExpert) {
+               elements[i].setAttribute("class","expert experthidden");
+           } else {
+               elements[i].setAttribute("class","expert");
+           }
+         }
+       }
+       function initCertForm() {
+               if(document.getElementById("placeholderName") == null) {
+                       return;
+               }
+               function getHint(id){
+                       var elem = document.getElementById(id);
+                       if(elem === null) {
+                               return null;
+                       }
+                       return $(elem).text();
+               }
+               $("select[name=profile]").off("change");
+               $("textarea[name=SANs]").off("keydown");
+               $("textarea[name=SANs]").get(0).modified=false;
+               $("input[name=CN]").off("keydown");
+               $("input[name=CN]").get(0).modified=false;
+
+               var placeholderName = getHint("placeholderName");
+               var defaultName = getHint("defaultName");
+               var defaultEmail = getHint("defaultEmail");
+               var defaultDomain = getHint("defaultDomain");
+               if(defaultName === null) {
+                       return;
+               }
+               $("textarea[name=SANs]").on("keydown", function(){
+                       this.modified = this.value !== "";
+               });
+               $("input[name=CN]").on("keydown", function(){
+                       this.modified = this.value !== "";
+               });
+
+               var loginCheck = document.getElementById("login");
+               $("select[name=profile]").change(function(){
+                       var val = this.value;
+                       var sans = $("textarea[name=SANs]").get(0);
+                       if(val.match(/client.*/)) {
+                               loginCheck.checked = true;
+                               loginCheck.disabled = false;
+                       } else {
+                               loginCheck.checked = false;
+                               loginCheck.disabled = true;
+                       }
+                       if(val.match(/client.*|mail.*/)) {
+                               if(!sans.modified) {
+                                       sans.value = "email:"+defaultEmail;
+                               }
+                       } else if(val.match(/server.*/)) {
+                               if(!sans.modified) {
+                                       sans.value = defaultDomain === null ? "" : "dns:" + defaultDomain;
+                               }
+                       }
+                       var cn = $("input[name=CN]").get(0);
+                       if(val.match(/.*-a/)) {
+                               if(!cn.modified) {
+                                       cn.value = defaultName;
+                               }
+                       }else{
+                               if(!cn.modified) {
+                                       cn.value = placeholderName;
+                               }
+                       }
+               });
+               var children = $("select[name=profile]").get(0).children;
+               var target = "client-mail";
+               for(var i=0; i < children.length; i++){
+                       if(children[i].value == "client-mail-a"){
+                               target = "client-mail-a";
+                       }
+               }
+
+               $("select[name=profile]").get(0).value = target;
+               $("select[name=profile]").trigger("change");
+
+       }
+       function init(){
+               showExpert(false);
+               initCertForm();
+               var expert = document.getElementById("expertbox");
+               if(expert !== null) {
+                       expert.onchange = (function(expert){return function(){showExpert(expert.checked)}})(expert);
+               }
+               $(".card-activatable").map(function() {
+                       var card = $(this);
+                       var refresh = function(){
+                               var radio = this.type == "radio";
+                               if(radio && this.form.currentRadios === undefined) {
+                                       this.form.currentRadios = {};
+                               }
+                               if(this.checked) {
+                                       card.find(".card-body").removeClass("d-none");
+                                       if(radio) {
+                                               var rds = this.form.currentRadios;
+                                               if(rds[this.name] !== undefined){
+                                                       $(rds[this.name]).trigger("change");
+                                               }
+                                               rds[this.name] = this;
+                                       }
+                               } else {
+                                       card.find(".card-body").addClass("d-none");
+                               }
+                       };
+                       card.find(".card-heading [type=\"checkbox\"]").map(refresh);
+                       card.find(".card-heading [type=\"checkbox\"]").change(refresh);
+                       card.find(".card-heading [type=\"radio\"]").map(refresh);
+                       card.find(".card-heading [type=\"radio\"]").change(refresh);
+                       return this.id;
+               });
+               initConfirm();
+       }
+       (function(oldLoad) {
+               if (oldLoad == undefined) {
+                       window.onload = init;
+               } else {
+                       window.onload = function() {
+                               init();
+                               oldLoad();
+                       }
+               }
+       })(window.onload);
+
+})();
index 936e1cb007fc6ac3ab11aff8a5690201a95143dc..a689be9ad41189148401b24b5da2a83f8226089c 100644 (file)
@@ -5,6 +5,7 @@
 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
 <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js" integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n" crossorigin="anonymous"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
+<script src="/static/js/expert.js"></script>
 <style type="text/css">
 .form-inline .motion {
   width: 100%;
index 61611adf1468151d0485800717371d9eb1aaa031..bc93bee4073e371f4dcec5bfb1e34fbc1ade3669 100644 (file)
 {%- endif %}
 
 {%- if motion.running %}
+{%- if may_cancel or may_finish %}
+<h3>{{_('Motion handling')}}</h3>
+
+{%- if may_cancel %}
+<form action="/motion/{{motion.identifier}}/cancel" method="POST" class="form-inline">
+<input type="text" placeholder="{{_('Cancelation reason')}}" name="reason" class="form-control" required="yes">
+<button type="submit" class="btn btn-danger btn-confirm" data-confirm="{{_('Do you really want to cancel the motion?')}}" data-reply="{{_('Cancel')}},{{_('Confirm')}}" name="cancel" value="cancel" id="cancel">{{_('Cancel')}}</button></br>
+</form>
+{%- endif %}
+{%- if may_finish %}
+<form action="/motion/{{motion.identifier}}/finish" method="POST" class="form-inline">
+<button type="submit" class="btn btn-danger btn-confirm" data-confirm="{{_('Do you really want to finish the motion?')}}" data-reply="{{_('Cancel')}},{{_('Confirm')}}" name="finish" value="finish" id="finish">{{_('Finish')}}</button></br>
+</form>
+{%- endif %}
+{%- endif %}
 
 {%- if may_vote %}
 <div class="panel panel-info" id="votes">
 {%- endfor %}
 {%- endif %}
 
-{%- if may_cancel %}
-<form action="/motion/{{motion.identifier}}/cancel" method="POST" class="form-inline">
-<input type="text" placeholder="{{_('Cancelation reason')}}" name="reason" class="form-control" required="yes">
-<button type="submit" class="btn btn-danger" name="cancel" value="cancel" id="cancel">{{_('Cancel')}}</button></br>
-</form>
-{%- endif %}
-{%- if may_finish %}
-<form action="/motion/{{motion.identifier}}/finish" method="POST" class="form-inline">
-<button type="submit" class="btn btn-danger" name="finish" value="finish" id="finish">{{_('Finish')}}</button></br>
-</form>
-{%- endif %}
+
 {%- endif %}
 <a href="/?start={{motion.id}}#motion-{{motion.id}}" class="btn btn-primary">{{_('Back')}}</a>
 {%- endblock %}
index b1ec4335b59fdb04154bb4bccccea2174ae40d76..436c06f446275a35c0a1b467e7722306475c3cf8 100644 (file)
@@ -406,12 +406,14 @@ class CreateMotionTests(BasicTest):
 
         motion='g1.20200402.004'
         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
-        testtext= '<button type="submit" class="btn btn-danger" name="cancel" value="cancel" id="cancel">Cancel</button>'
+        testtext= '<button type="submit" class="btn btn-danger btn-confirm" data-confirm="Do you really want to cancel the motion?" '\
+            + 'data-reply="Cancel,Confirm" name="cancel" value="cancel" id="cancel">Cancel</button>'
         self.assertIn(str.encode(testtext), result.data)
 
         motion='g1.20200402.004'
         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': 'testuser/vote:*'}, follow_redirects=True)
-        testtext= '<button type="submit" class="btn btn-danger" name="cancel" value="cancel" id="cancel">Cancel</button>'
+        testtext= '<button type="submit" class="btn btn-danger btn-confirm" data-confirm="Do you really want to cancel the motion?" '\
+            + 'data-reply="Cancel,Confirm" name="cancel" value="cancel" id="cancel">Cancel</button>'
         self.assertNotIn(str.encode(testtext), result.data)
 
     def test_cancelMotion(self):
@@ -451,12 +453,14 @@ class CreateMotionTests(BasicTest):
 
         motion='g1.20200402.004'
         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': user}, follow_redirects=True)
-        testtext= '<button type="submit" class="btn btn-danger" name="finish" value="finish" id="finish">Finish</button>'
+        testtext= '<button type="submit" class="btn btn-danger btn-confirm" data-confirm="Do you really want to finish the motion?" '\
+            + 'data-reply="Cancel,Confirm" name="finish" value="finish" id="finish">Finish</button>'
         self.assertIn(str.encode(testtext), result.data)
 
         motion='g1.20200402.004'
         result = self.app.get('/motion/' + motion, environ_base={'USER_ROLES': 'testuser/vote:*'}, follow_redirects=True)
-        testtext= '<button type="submit" class="btn btn-danger" name="finish" value="finish" id="finish">Finish</button>'
+        testtext= '<button type="submit" class="btn btn-danger btn-confirm" data-confirm="Do you really want to finish the motion?" '\
+            + 'data-reply="Cancel,Confirm" name="finish" value="finish" id="finish">Finish</button>'
         self.assertNotIn(str.encode(testtext), result.data)
 
     def test_finishMotion(self):