liquid_feedback_frontend
changeset 5:afd9f769c7ae beta1
Version beta1
Final voting with Schulze-Method is now possible
Many bug fixes and code cleanup
Registration with invite codes
More sort and filter options
Seperated display of "supporters" and "potential supporters"
Optical changes
Flood limit / initiative contigent is now checked by frontend
Neccessary changes to access core beta11
Final voting with Schulze-Method is now possible
Many bug fixes and code cleanup
Registration with invite codes
More sort and filter options
Seperated display of "supporters" and "potential supporters"
Optical changes
Flood limit / initiative contigent is now checked by frontend
Neccessary changes to access core beta11
line diff
1.1 --- a/app/main/_filter/21_auth.lua Thu Dec 10 12:00:00 2009 +0100 1.2 +++ b/app/main/_filter/21_auth.lua Fri Dec 25 12:00:00 2009 +0100 1.3 @@ -1,9 +1,11 @@ 1.4 local auth_needed = not ( 1.5 request.get_module() == 'index' 1.6 and ( 1.7 - request.get_view() == 'login' 1.8 + request.get_view() == 'login' 1.9 or request.get_action() == 'login' 1.10 - or request.get_view() == "about" 1.11 + or request.get_view() == "register" 1.12 + or request.get_action() == "register" 1.13 + or request.get_view() == "about" 1.14 ) 1.15 ) 1.16
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/app/main/_filter_action/10_transaction.lua Fri Dec 25 12:00:00 2009 +0100 2.3 @@ -0,0 +1,5 @@ 2.4 +db:query("BEGIN;") 2.5 + 2.6 +execute.inner() 2.7 + 2.8 +db:query("COMMIT;") 2.9 \ No newline at end of file
3.1 --- a/app/main/_filter_view/30_navigation.lua Thu Dec 10 12:00:00 2009 +0100 3.2 +++ b/app/main/_filter_view/30_navigation.lua Fri Dec 25 12:00:00 2009 +0100 3.3 @@ -6,13 +6,21 @@ 3.4 ui.link{ 3.5 content = function() 3.6 ui.image{ static = "icons/16/key.png" } 3.7 - slot.put('Login') 3.8 + slot.put(_"Login") 3.9 end, 3.10 module = 'index', 3.11 view = 'login' 3.12 } 3.13 ui.link{ 3.14 content = function() 3.15 + ui.image{ static = "icons/16/book_edit.png" } 3.16 + slot.put(_"Registration") 3.17 + end, 3.18 + module = 'index', 3.19 + view = 'register' 3.20 + } 3.21 + ui.link{ 3.22 + content = function() 3.23 ui.image{ static = "icons/16/information.png" } 3.24 slot.put('About / Impressum') 3.25 end, 3.26 @@ -29,7 +37,7 @@ 3.27 ui.link{ 3.28 content = function() 3.29 ui.image{ static = "icons/16/house.png" } 3.30 - slot.put(_'Home') 3.31 + slot.put(_"Home") 3.32 end, 3.33 module = 'index', 3.34 view = 'index' 3.35 @@ -38,7 +46,7 @@ 3.36 ui.link{ 3.37 content = function() 3.38 ui.image{ static = "icons/16/package.png" } 3.39 - slot.put(_'Areas') 3.40 + slot.put(_"Areas") 3.41 end, 3.42 module = 'area', 3.43 view = 'list' 3.44 @@ -47,7 +55,7 @@ 3.45 ui.link{ 3.46 content = function() 3.47 ui.image{ static = "icons/16/group.png" } 3.48 - slot.put(_'Members') 3.49 + slot.put(_"Members") 3.50 end, 3.51 module = 'member', 3.52 view = 'list' 3.53 @@ -56,7 +64,7 @@ 3.54 ui.link{ 3.55 content = function() 3.56 ui.image{ static = "icons/16/book_edit.png" } 3.57 - slot.put(_'Contacts') 3.58 + slot.put(_"Contacts") 3.59 end, 3.60 module = 'contact', 3.61 view = 'list' 3.62 @@ -65,7 +73,7 @@ 3.63 ui.link{ 3.64 content = function() 3.65 ui.image{ static = "icons/16/information.png" } 3.66 - slot.put(_'About') 3.67 + slot.put(_"About") 3.68 end, 3.69 module = 'index', 3.70 view = 'about' 3.71 @@ -74,7 +82,7 @@ 3.72 ui.link{ 3.73 content = function() 3.74 ui.image{ static = "icons/16/bug.png" } 3.75 - slot.put(_'Bug report') 3.76 + slot.put(_"Bug report") 3.77 end, 3.78 external = "http://trac.public-software-group.org/projects/lf" --/newticket?description=" .. encode.url_part("\n\n\n\nReport for: " .. os.getenv("REQUEST_URI") ) 3.79 }
4.1 --- a/app/main/_layout/default.html Thu Dec 10 12:00:00 2009 +0100 4.2 +++ b/app/main/_layout/default.html Fri Dec 25 12:00:00 2009 +0100 4.3 @@ -38,12 +38,12 @@ 4.4 <div class="path" id="path"> 4.5 <!-- WEBMCP SLOT path --> 4.6 </div> 4.7 + <div class="help_hidden" id="help_hidden"> 4.8 + <!-- WEBMCP SLOT help_hidden --> 4.9 + </div> 4.10 <div class="title" id="title"> 4.11 <!-- WEBMCP SLOT title --> 4.12 </div> 4.13 - <div class="help_hidden" id="help_hidden"> 4.14 - <!-- WEBMCP SLOT help_hidden --> 4.15 - </div> 4.16 <div class="actions" id="actions"> 4.17 <div class="interest vote_info" id="interest"> 4.18 <!-- WEBMCP SLOT interest -->
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/app/main/admin/_action/area_update.lua Fri Dec 25 12:00:00 2009 +0100 5.3 @@ -0,0 +1,19 @@ 5.4 +if not app.session.member.admin then 5.5 + error() 5.6 +end 5.7 + 5.8 +local id = param.get_id() 5.9 + 5.10 +local area 5.11 +if id then 5.12 + area = Area:new_selector():add_where{ "id = ?", id }:single_object_mode():exec() 5.13 +else 5.14 + area = Area:new() 5.15 +end 5.16 + 5.17 + 5.18 +param.update(area, "name", "description", "active") 5.19 + 5.20 +area:save() 5.21 + 5.22 +slot.put_into("notice", _"Area successfully updated")
6.1 --- a/app/main/admin/area_show.lua Thu Dec 10 12:00:00 2009 +0100 6.2 +++ b/app/main/admin/area_show.lua Fri Dec 25 12:00:00 2009 +0100 6.3 @@ -10,8 +10,8 @@ 6.4 ui.form{ 6.5 attr = { class = "vertical" }, 6.6 record = area, 6.7 - module = "area", 6.8 - action = "update", 6.9 + module = "admin", 6.10 + action = "area_update", 6.11 routing = { 6.12 default = { 6.13 mode = "redirect",
7.1 --- a/app/main/area/_action/update.lua Thu Dec 10 12:00:00 2009 +0100 7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 7.3 @@ -1,19 +0,0 @@ 7.4 -if not app.session.member.admin then 7.5 - error() 7.6 -end 7.7 - 7.8 -local id = param.get_id() 7.9 - 7.10 -local area 7.11 -if id then 7.12 - area = Area:new_selector():add_where{ "id = ?", id }:single_object_mode():exec() 7.13 -else 7.14 - area = Area:new() 7.15 -end 7.16 - 7.17 - 7.18 -param.update(area, "name", "description", "active") 7.19 - 7.20 -area:save() 7.21 - 7.22 -slot.put_into("notice", _"Area successfully updated")
8.1 --- a/app/main/contact/_action/add_member.lua Thu Dec 10 12:00:00 2009 +0100 8.2 +++ b/app/main/contact/_action/add_member.lua Fri Dec 25 12:00:00 2009 +0100 8.3 @@ -20,8 +20,3 @@ 8.4 contact.public = public or false 8.5 contact:save() 8.6 8.7 -if public then 8.8 --- slot.put_into("notice", _"Member has been saved as public contact") 8.9 -else 8.10 --- slot.put_into("notice", _"Member has been saved as private contact") 8.11 -end
9.1 --- a/app/main/delegation/_action/update.lua Thu Dec 10 12:00:00 2009 +0100 9.2 +++ b/app/main/delegation/_action/update.lua Fri Dec 25 12:00:00 2009 +0100 9.3 @@ -15,9 +15,9 @@ 9.4 if param.get("delete") or trustee_id == -1 then 9.5 9.6 if delegation then 9.7 - 9.8 + 9.9 delegation:destroy() 9.10 - 9.11 + 9.12 if issue_id then 9.13 slot.put_into("notice", _"Your delegation for this issue has been deleted.") 9.14 elseif area_id then 9.15 @@ -27,7 +27,7 @@ 9.16 end 9.17 9.18 end 9.19 - 9.20 + 9.21 else 9.22 9.23 if not delegation then 9.24 @@ -35,6 +35,13 @@ 9.25 delegation.truster_id = truster_id 9.26 delegation.area_id = area_id 9.27 delegation.issue_id = issue_id 9.28 + if issue_id then 9.29 + delegation.scope = "issue" 9.30 + elseif area_id then 9.31 + delegation.scope = "area" 9.32 + else 9.33 + delegation.scope = "global" 9.34 + end 9.35 end 9.36 9.37 delegation.trustee_id = trustee_id
10.1 --- a/app/main/delegation/_show_box.lua Thu Dec 10 12:00:00 2009 +0100 10.2 +++ b/app/main/delegation/_show_box.lua Fri Dec 25 12:00:00 2009 +0100 10.3 @@ -1,48 +1,47 @@ 10.4 slot.select("actions", function() 10.5 10.6 - ui.container{ 10.7 - attr = { class = "delegation vote_info"}, 10.8 - content = function() 10.9 - 10.10 - local delegation 10.11 - local area_id 10.12 - local issue_id 10.13 - 10.14 - local scope = "global" 10.15 - 10.16 - if param.get("initiative_id", atom.integer) then 10.17 - issue_id = Initiative:by_id(param.get("initiative_id", atom.integer)).issue_id 10.18 - scope = "issue" 10.19 - end 10.20 - 10.21 - if param.get("issue_id", atom.integer) then 10.22 - issue_id = param.get("issue_id", atom.integer) 10.23 - scope = "issue" 10.24 - end 10.25 - 10.26 - if param.get("area_id", atom.integer) then 10.27 - area_id = param.get("area_id", atom.integer) 10.28 - scope = "area" 10.29 - end 10.30 - 10.31 - 10.32 - 10.33 - local delegation 10.34 - 10.35 - if issue_id then 10.36 - delegation = Delegation:by_pk(app.session.member.id, nil, issue_id) 10.37 - if not delegation then 10.38 - local issue = Issue:by_id(issue_id) 10.39 - delegation = Delegation:by_pk(app.session.member.id, issue.area_id) 10.40 - end 10.41 - elseif area_id then 10.42 - delegation = Delegation:by_pk(app.session.member.id, area_id) 10.43 - end 10.44 - 10.45 - if not delegation then 10.46 - delegation = Delegation:by_pk(app.session.member.id) 10.47 - end 10.48 - if delegation then 10.49 + local delegation 10.50 + local area_id 10.51 + local issue_id 10.52 + 10.53 + local scope = "global" 10.54 + 10.55 + if param.get("initiative_id", atom.integer) then 10.56 + issue_id = Initiative:by_id(param.get("initiative_id", atom.integer)).issue_id 10.57 + scope = "issue" 10.58 + end 10.59 + 10.60 + if param.get("issue_id", atom.integer) then 10.61 + issue_id = param.get("issue_id", atom.integer) 10.62 + scope = "issue" 10.63 + end 10.64 + 10.65 + if param.get("area_id", atom.integer) then 10.66 + area_id = param.get("area_id", atom.integer) 10.67 + scope = "area" 10.68 + end 10.69 + 10.70 + 10.71 + 10.72 + local delegation 10.73 + local issue 10.74 + if issue_id then 10.75 + issue = Issue:by_id(issue_id) 10.76 + delegation = Delegation:by_pk(app.session.member.id, nil, issue_id) 10.77 + if not delegation then 10.78 + delegation = Delegation:by_pk(app.session.member.id, issue.area_id) 10.79 + end 10.80 + elseif area_id then 10.81 + delegation = Delegation:by_pk(app.session.member.id, area_id) 10.82 + end 10.83 + 10.84 + if not delegation then 10.85 + delegation = Delegation:by_pk(app.session.member.id) 10.86 + end 10.87 + if delegation then 10.88 + ui.container{ 10.89 + attr = { class = "delegation vote_info"}, 10.90 + content = function() 10.91 ui.container{ 10.92 attr = { 10.93 title = _"Click for details", 10.94 @@ -117,25 +116,27 @@ 10.95 end 10.96 } 10.97 if i == 2 then 10.98 - ui.link{ 10.99 - attr = { class = "revoke" }, 10.100 - content = function() 10.101 - ui.image{ static = "icons/16/delete.png" } 10.102 - slot.put(_"Revoke") 10.103 - end, 10.104 - module = "delegation", 10.105 - action = "update", 10.106 - params = { issue_id = delegation.issue_id, area_id = delegation.area_id, delete = true }, 10.107 - routing = { 10.108 - default = { 10.109 - mode = "redirect", 10.110 - module = request.get_module(), 10.111 - view = request.get_view(), 10.112 - id = param.get_id_cgi(), 10.113 - params = param.get_all_cgi() 10.114 + if not issue or (issue.state ~= "finished" and issue.state ~= "cancelled") then 10.115 + ui.link{ 10.116 + attr = { class = "revoke" }, 10.117 + content = function() 10.118 + ui.image{ static = "icons/16/delete.png" } 10.119 + slot.put(_"Revoke") 10.120 + end, 10.121 + module = "delegation", 10.122 + action = "update", 10.123 + params = { issue_id = delegation.issue_id, area_id = delegation.area_id, delete = true }, 10.124 + routing = { 10.125 + default = { 10.126 + mode = "redirect", 10.127 + module = request.get_module(), 10.128 + view = request.get_view(), 10.129 + id = param.get_id_cgi(), 10.130 + params = param.get_all_cgi() 10.131 + } 10.132 } 10.133 } 10.134 - } 10.135 + end 10.136 end 10.137 end 10.138 } 10.139 @@ -163,30 +164,39 @@ 10.140 end 10.141 } 10.142 end 10.143 - ui.link{ 10.144 - content = function() 10.145 + } 10.146 + 10.147 + 10.148 + end 10.149 + ui.link{ 10.150 + content = function() 10.151 + if scope == "global" and delegation then 10.152 + ui.image{ static = "icons/16/table_go.png" } 10.153 + slot.put(_"Change global delegation") 10.154 + elseif scope == "global" and not delegation then 10.155 + ui.image{ static = "icons/16/table_go.png" } 10.156 + slot.put(_"Set global delegation") 10.157 + elseif scope == "area" and delegation and delegation.area_id then 10.158 + ui.image{ static = "icons/16/table_go.png" } 10.159 + slot.put(_"Change area delegation") 10.160 + elseif scope == "area" and not (delegation and delegation.area_id) then 10.161 + ui.image{ static = "icons/16/table_go.png" } 10.162 + slot.put(_"Set area delegation") 10.163 + elseif scope == "issue" then 10.164 + if delegation and delegation.issue_id then 10.165 ui.image{ static = "icons/16/table_go.png" } 10.166 - if scope == "global" and delegation then 10.167 - slot.put(_"Change global delegation") 10.168 - elseif scope == "global" and not delegation then 10.169 - slot.put(_"Set global delegation") 10.170 - elseif scope == "area" and delegation and delegation.area_id then 10.171 - slot.put(_"Change area delegation") 10.172 - elseif scope == "area" and not (delegation and delegation.area_id) then 10.173 - slot.put(_"Set area delegation") 10.174 - elseif scope == "issue" and delegation and delegation.issue_id then 10.175 - slot.put(_"Change issue delegation") 10.176 - elseif scope == "issue" and not (delegation and delegation.issue_id) then 10.177 - slot.put(_"Set issue delegation") 10.178 - end 10.179 - end, 10.180 - module = "delegation", 10.181 - view = "new", 10.182 - params = { 10.183 - area_id = area_id, 10.184 - issue_id = issue_id 10.185 - } 10.186 - } 10.187 - end 10.188 + slot.put(_"Change issue delegation") 10.189 + elseif issue.state ~= "finished" and issue.state ~= "cancelled" then 10.190 + ui.image{ static = "icons/16/table_go.png" } 10.191 + slot.put(_"Set issue delegation") 10.192 + end 10.193 + end 10.194 + end, 10.195 + module = "delegation", 10.196 + view = "new", 10.197 + params = { 10.198 + area_id = area_id, 10.199 + issue_id = issue_id 10.200 + } 10.201 } 10.202 end)
11.1 --- a/app/main/draft/_action/add.lua Thu Dec 10 12:00:00 2009 +0100 11.2 +++ b/app/main/draft/_action/add.lua Fri Dec 25 12:00:00 2009 +0100 11.3 @@ -1,5 +1,22 @@ 11.4 +local tmp = db:query({ "SELECT text_entries_left FROM member_contingent_left WHERE member_id = ?", app.session.member.id }, "opt_object") 11.5 +if tmp and tmp.text_entries_left and tmp.text_entries_left < 1 then 11.6 + slot.put_into("error", _"Sorry, you have reached your personal flood limit. Please be slower...") 11.7 + return false 11.8 +end 11.9 + 11.10 local initiative = Initiative:by_id(param.get("initiative_id", atom.integer)) 11.11 11.12 +-- TODO important m1 selectors returning result _SET_! 11.13 +local issue = initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() 11.14 + 11.15 +if issue.closed then 11.16 + slot.put_into("error", _"This issue is already closed.") 11.17 + return false 11.18 +elseif issue.half_frozen then 11.19 + slot.put_into("error", _"This issue is already frozen.") 11.20 + return false 11.21 +end 11.22 + 11.23 if Initiator:by_pk(initiative.id, app.session.member.id) then 11.24 local draft = Draft:new() 11.25 draft.author_id = app.session.member.id
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/app/main/index/_action/register.lua Fri Dec 25 12:00:00 2009 +0100 12.3 @@ -0,0 +1,132 @@ 12.4 +local invite_code = InviteCode:by_code(param.get("code")) 12.5 + 12.6 +if not invite_code or invite_code.used then 12.7 + slot.put_into("error", _"The code you've entered is invalid") 12.8 + request.redirect{ 12.9 + mode = "forward", 12.10 + module = "index", 12.11 + view = "register" 12.12 + } 12.13 + return false 12.14 +end 12.15 + 12.16 +local name = param.get("name") 12.17 + 12.18 +if invite_code and not name then 12.19 + slot.put_into("notice", _"Invite code valid!") 12.20 + request.redirect{ 12.21 + mode = "redirect", 12.22 + module = "index", 12.23 + view = "register", 12.24 + params = { code = invite_code.code } 12.25 + } 12.26 + return false 12.27 +end 12.28 + 12.29 +if Member:by_name(name) then 12.30 + slot.put_into("error", _"This name is already taken, please choose another one!") 12.31 + request.redirect{ 12.32 + mode = "redirect", 12.33 + module = "index", 12.34 + view = "register", 12.35 + params = { code = invite_code.code } 12.36 + } 12.37 + return false 12.38 +end 12.39 + 12.40 +local login = param.get("login") 12.41 + 12.42 +if name and not login then 12.43 + slot.put_into("notice", _"Name is available") 12.44 + request.redirect{ 12.45 + mode = "redirect", 12.46 + module = "index", 12.47 + view = "register", 12.48 + params = { 12.49 + code = invite_code.code, 12.50 + name = name 12.51 + } 12.52 + } 12.53 + return false 12.54 +end 12.55 + 12.56 +if Member:by_login(login) then 12.57 + slot.put_into("error", _"This login is already taken, please choose another one!") 12.58 + request.redirect{ 12.59 + mode = "redirect", 12.60 + module = "index", 12.61 + view = "register", 12.62 + params = { 12.63 + code = invite_code.code, 12.64 + name = name 12.65 + } 12.66 + } 12.67 + return false 12.68 +end 12.69 + 12.70 +local password1 = param.get("password1") 12.71 +local password2 = param.get("password2") 12.72 + 12.73 +if login and not password1 then 12.74 + slot.put_into("notice", _"Login is available") 12.75 + request.redirect{ 12.76 + mode = "redirect", 12.77 + module = "index", 12.78 + view = "register", 12.79 + params = { 12.80 + code = invite_code.code, 12.81 + name = name, 12.82 + login = login 12.83 + } 12.84 + } 12.85 + return false 12.86 +end 12.87 + 12.88 +if password1 ~= password2 then 12.89 + slot.put_into("error", _"Passwords don't match!") 12.90 + request.redirect{ 12.91 + mode = "redirect", 12.92 + module = "index", 12.93 + view = "register", 12.94 + params = { 12.95 + code = invite_code.code, 12.96 + name = name, 12.97 + login = login 12.98 + } 12.99 + } 12.100 + return false 12.101 +end 12.102 + 12.103 +if #password1 < 8 then 12.104 + slot.put_into("error", _"Passwords must consist of at least 8 characters!") 12.105 + request.redirect{ 12.106 + mode = "redirect", 12.107 + module = "index", 12.108 + view = "register", 12.109 + params = { 12.110 + code = invite_code.code, 12.111 + name = name, 12.112 + login = login 12.113 + } 12.114 + } 12.115 + return false 12.116 +end 12.117 + 12.118 +local member = Member:new() 12.119 + 12.120 +member.login = login 12.121 +member.name = name 12.122 +member:set_password(password1) 12.123 +member:save() 12.124 + 12.125 +invite_code.member_id = member.id 12.126 +invite_code.used = "now" 12.127 +invite_code:save() 12.128 + 12.129 +slot.put_into("notice", _"You've successfully registered and you can login now with your login and password!") 12.130 + 12.131 + request.redirect{ 12.132 + mode = "redirect", 12.133 + module = "index", 12.134 + view = "login", 12.135 + }
13.1 --- a/app/main/index/index.lua Thu Dec 10 12:00:00 2009 +0100 13.2 +++ b/app/main/index/index.lua Fri Dec 25 12:00:00 2009 +0100 13.3 @@ -96,6 +96,90 @@ 13.4 13.5 util.help("index.index", _"Home") 13.6 13.7 + 13.8 +local selector = Area:new_selector() 13.9 + :reset_fields() 13.10 + :add_field("area.id", nil, { "grouped" }) 13.11 + :add_field("area.name", nil, { "grouped" }) 13.12 + :add_field("membership.member_id NOTNULL", "is_member", { "grouped" }) 13.13 + :add_field("count(issue.id)", "issues_to_vote_count") 13.14 + :add_field("count(interest.member_id)", "interested_issues_to_vote_count") 13.15 + :join("issue", nil, "issue.area_id = area.id AND issue.fully_frozen NOTNULL AND issue.closed ISNULL") 13.16 + :left_join("direct_voter", nil, { "direct_voter.issue_id = issue.id AND direct_voter.member_id = ?", app.session.member.id }) 13.17 + :add_where{ "direct_voter.member_id ISNULL" } 13.18 + :left_join("interest", nil, { "interest.issue_id = issue.id AND interest.member_id = ?", app.session.member.id }) 13.19 + :left_join("membership", nil, { "membership.area_id = area.id AND membership.member_id = ? ", app.session.member.id }) 13.20 + 13.21 +local areas = {} 13.22 +for i, area in ipairs(selector:exec()) do 13.23 + if area.is_member or area.interested_issues_to_vote_count > 0 then 13.24 + areas[#areas+1] = area 13.25 + end 13.26 +end 13.27 + 13.28 +if #areas > 0 then 13.29 + ui.container{ 13.30 + attr = { style = "font-weight: bold;" }, 13.31 + content = _"Current votings in areas you are member of and issues you are interested in:" 13.32 + } 13.33 + 13.34 + ui.list{ 13.35 + records = areas, 13.36 + columns = { 13.37 + { 13.38 + name = "name" 13.39 + }, 13.40 + { 13.41 + content = function(record) 13.42 + if record.is_member and record.issues_to_vote_count > 0 then 13.43 + ui.link{ 13.44 + content = function() 13.45 + if record.issues_to_vote_count > 1 then 13.46 + slot.put(_("#{issues_to_vote_count} issue(s)", { issues_to_vote_count = record.issues_to_vote_count })) 13.47 + else 13.48 + slot.put(_("One issue")) 13.49 + end 13.50 + end, 13.51 + module = "area", 13.52 + view = "show", 13.53 + id = record.id, 13.54 + params = { 13.55 + filter = "frozen", 13.56 + filter_voting = "not_voted" 13.57 + } 13.58 + } 13.59 + else 13.60 + slot.put(_"Not a member") 13.61 + end 13.62 + end 13.63 + }, 13.64 + { 13.65 + content = function(record) 13.66 + if record.interested_issues_to_vote_count > 0 then 13.67 + ui.link{ 13.68 + content = function() 13.69 + if record.interested_issues_to_vote_count > 1 then 13.70 + slot.put(_("#{interested_issues_to_vote_count} issue(s) you are interested in", { interested_issues_to_vote_count = record.interested_issues_to_vote_count })) 13.71 + else 13.72 + slot.put(_"One issue you are interested in") 13.73 + end 13.74 + end, 13.75 + module = "area", 13.76 + view = "show", 13.77 + id = record.id, 13.78 + params = { 13.79 + filter = "frozen", 13.80 + filter_interest = "my", 13.81 + filter_voting = "not_voted" 13.82 + } 13.83 + } 13.84 + end 13.85 + end 13.86 + }, 13.87 + } 13.88 + } 13.89 +end 13.90 + 13.91 execute.view{ 13.92 module = "member", 13.93 view = "_show",
14.1 --- a/app/main/index/login.lua Thu Dec 10 12:00:00 2009 +0100 14.2 +++ b/app/main/index/login.lua Fri Dec 25 12:00:00 2009 +0100 14.3 @@ -1,3 +1,15 @@ 14.4 +local warning_text = _"Some JavaScript based functions (voting in particular) will not work.\nFor this beta, please use a current version of Firefox, Safari, Opera(?), Konqueror or another (more) standard compliant browser.\nAlternative access without JavaScript will be available soon." 14.5 + 14.6 +ui.script{ static = "js/browser_warning.js" } 14.7 +ui.script{ script = "checkBrowser(" .. encode.json(_"Your web browser is not fully supported yet." .. " " .. warning_text:gsub("\n", "\n\n")) .. ");" } 14.8 + 14.9 +ui.tag{ 14.10 + tag = "noscript", 14.11 + content = function() 14.12 + slot.put(_"JavaScript is disabled or not available." .. " " .. encode.html_newlines(warning_text)) 14.13 + end 14.14 +} 14.15 + 14.16 slot.put_into("title", encode.html(config.app_title)) 14.17 14.18 ui.tag{ 14.19 @@ -24,7 +36,7 @@ 14.20 content = function() 14.21 ui.field.text{ 14.22 attr = { id = "username_field" }, 14.23 - label = _'Username', 14.24 + label = _'login name', 14.25 html_name = 'login', 14.26 value = '' 14.27 }
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 15.2 +++ b/app/main/index/register.lua Fri Dec 25 12:00:00 2009 +0100 15.3 @@ -0,0 +1,96 @@ 15.4 +slot.put_into("title", _"Registration") 15.5 + 15.6 +slot.select("actions", function() 15.7 + ui.link{ 15.8 + content = function() 15.9 + ui.image{ static = "icons/16/cancel.png" } 15.10 + slot.put(_"Cancel") 15.11 + end, 15.12 + module = "index", 15.13 + view = "index" 15.14 + } 15.15 +end) 15.16 + 15.17 +local code = param.get("code") 15.18 +local name = param.get("name") 15.19 +local login = param.get("login") 15.20 + 15.21 +ui.form{ 15.22 + attr = { class = "login" }, 15.23 + module = 'index', 15.24 + action = 'register', 15.25 + params = { 15.26 + code = code, 15.27 + name = name, 15.28 + login = login 15.29 + }, 15.30 + content = function() 15.31 + 15.32 + if not code then 15.33 + ui.tag{ 15.34 + tag = "p", 15.35 + content = _"Please enter the invite code you've received." 15.36 + } 15.37 + ui.field.text{ 15.38 + label = _'Invite code', 15.39 + name = 'code', 15.40 + } 15.41 + 15.42 + elseif not name then 15.43 + ui.tag{ 15.44 + tag = "p", 15.45 + content = _"Please choose a name, i.e. your real name or your nick name. This name will be shown to others to identify you. You CAN'T change this name later, so please choose it wisely!" 15.46 + } 15.47 + ui.field.text{ 15.48 + label = _'Name', 15.49 + name = 'name', 15.50 + value = param.get("name") 15.51 + } 15.52 + 15.53 + elseif not login then 15.54 + ui.tag{ 15.55 + tag = "p", 15.56 + content = _"Please choose a login name. This name will not be shown to others and is used only by you to login into the system. The login name is case sensitive." 15.57 + } 15.58 + ui.field.text{ 15.59 + label = _'Login name', 15.60 + name = 'login', 15.61 + value = param.get("login") 15.62 + } 15.63 + 15.64 + else 15.65 + ui.field.text{ 15.66 + label = _'Name', 15.67 + name = 'name', 15.68 + value = param.get("name"), 15.69 + readonly = true 15.70 + } 15.71 + ui.field.text{ 15.72 + label = _'Login name', 15.73 + name = 'login', 15.74 + value = param.get("login"), 15.75 + readonly = true 15.76 + } 15.77 + ui.tag{ 15.78 + tag = "p", 15.79 + content = _"Please choose a password and enter it twice. The password is case sensitive." 15.80 + } 15.81 + ui.field.password{ 15.82 + label = _'Password', 15.83 + name = 'password1', 15.84 + } 15.85 + ui.field.password{ 15.86 + label = _'Password (repeat)', 15.87 + name = 'password2', 15.88 + } 15.89 + 15.90 + end 15.91 + 15.92 + ui.submit{ 15.93 + text = _'Register' 15.94 + } 15.95 + 15.96 + end 15.97 +} 15.98 + 15.99 +
16.1 --- a/app/main/initiative/_action/add_support.lua Thu Dec 10 12:00:00 2009 +0100 16.2 +++ b/app/main/initiative/_action/add_support.lua Fri Dec 25 12:00:00 2009 +0100 16.3 @@ -1,5 +1,16 @@ 16.4 local initiative = Initiative:new_selector():add_where{ "id = ?", param.get_id()}:single_object_mode():exec() 16.5 16.6 +-- TODO important m1 selectors returning result _SET_! 16.7 +local issue = initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() 16.8 + 16.9 +if issue.closed then 16.10 + slot.put_into("error", _"This issue is already closed.") 16.11 + return false 16.12 +elseif issue.fully_frozen then 16.13 + slot.put_into("error", _"Voting for this issue has already begun.") 16.14 + return false 16.15 +end 16.16 + 16.17 local member = app.session.member 16.18 16.19 local supporter = Supporter:by_pk(initiative.id, member.id)
17.1 --- a/app/main/initiative/_action/create.lua Thu Dec 10 12:00:00 2009 +0100 17.2 +++ b/app/main/initiative/_action/create.lua Fri Dec 25 12:00:00 2009 +0100 17.3 @@ -1,13 +1,29 @@ 17.4 +local tmp = db:query({ "SELECT text_entries_left, initiatives_left FROM member_contingent_left WHERE member_id = ?", app.session.member.id }, "opt_object") 17.5 +if tmp then 17.6 + if tmp.initiatives_left and tmp.initiatives_left < 1 then 17.7 + slot.put_into("error", _"Sorry, your contingent for creating initiatives has been used up. Please try again later.") 17.8 + return false 17.9 + end 17.10 + if tmp.text_entries_left and tmp.text_entries_left < 1 then 17.11 + slot.put_into("error", _"Sorry, you have reached your personal flood limit. Please be slower...") 17.12 + return false 17.13 + end 17.14 +end 17.15 + 17.16 local issue 17.17 local area 17.18 17.19 -db:query("BEGIN") 17.20 - 17.21 local issue_id = param.get("issue_id", atom.integer) 17.22 if issue_id then 17.23 issue = Issue:new_selector():add_where{"id=?",issue_id}:single_object_mode():exec() 17.24 + if issue.closed then 17.25 + slot.put_into("error", _"This issue is already closed.") 17.26 + return false 17.27 + elseif issue.fully_frozen then 17.28 + slot.put_into("error", _"Voting for this issue has already begun.") 17.29 + return false 17.30 + end 17.31 area = issue.area 17.32 - 17.33 else 17.34 local area_id = param.get("area_id", atom.integer) 17.35 area = Area:new_selector():add_where{"id=?",area_id}:single_object_mode():exec() 17.36 @@ -22,6 +38,8 @@ 17.37 issue:save() 17.38 end 17.39 17.40 + 17.41 + 17.42 initiative.issue_id = issue.id 17.43 17.44 param.update(initiative, "name", "discussion_url") 17.45 @@ -55,8 +73,6 @@ 17.46 supporter.draft_id = draft.id 17.47 supporter:save() 17.48 17.49 -db:query("COMMIT") 17.50 - 17.51 slot.put_into("notice", _"Initiative successfully created") 17.52 17.53 request.redirect{
18.1 --- a/app/main/initiative/_action/remove_support.lua Thu Dec 10 12:00:00 2009 +0100 18.2 +++ b/app/main/initiative/_action/remove_support.lua Fri Dec 25 12:00:00 2009 +0100 18.3 @@ -1,5 +1,16 @@ 18.4 local initiative = Initiative:new_selector():add_where{ "id = ?", param.get_id()}:single_object_mode():exec() 18.5 18.6 +-- TODO important m1 selectors returning result _SET_! 18.7 +local issue = initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() 18.8 + 18.9 +if issue.closed then 18.10 + slot.put_into("error", _"This issue is already closed.") 18.11 + return false 18.12 +elseif issue.fully_frozen then 18.13 + slot.put_into("error", _"Voting for this issue has already begun.") 18.14 + return false 18.15 +end 18.16 + 18.17 local member = app.session.member 18.18 18.19 local supporter = Supporter:by_pk(initiative.id, member.id)
19.1 --- a/app/main/initiative/_list.lua Thu Dec 10 12:00:00 2009 +0100 19.2 +++ b/app/main/initiative/_list.lua Fri Dec 25 12:00:00 2009 +0100 19.3 @@ -9,32 +9,32 @@ 19.4 order_options[#order_options+1] = { 19.5 name = "rank", 19.6 label = _"Rank", 19.7 - order_by = "initiative.rank" 19.8 + order_by = "initiative.rank, initiative.admitted DESC, vote_ratio(initiative.positive_votes, initiative.negative_votes) DESC, initiative.id" 19.9 } 19.10 end 19.11 19.12 order_options[#order_options+1] = { 19.13 + name = "potential_support", 19.14 + label = _"Potential support", 19.15 + order_by = "initiative.supporter_count::float / issue.population::float DESC, initiative.id" 19.16 +} 19.17 + 19.18 +order_options[#order_options+1] = { 19.19 name = "support", 19.20 label = _"Support", 19.21 - order_by = "initiative.supporter_count::float / issue.population::float DESC" 19.22 -} 19.23 - 19.24 -order_options[#order_options+1] = { 19.25 - name = "support_si", 19.26 - label = _"Support S+I", 19.27 - order_by = "initiative.satisfied_informed_supporter_count::float / issue.population::float DESC" 19.28 + order_by = "initiative.satisfied_supporter_count::float / issue.population::float DESC, initiative.id" 19.29 } 19.30 19.31 order_options[#order_options+1] = { 19.32 name = "newest", 19.33 label = _"Newest", 19.34 - order_by = "initiative.created DESC" 19.35 + order_by = "initiative.created DESC, initiative.id" 19.36 } 19.37 19.38 order_options[#order_options+1] = { 19.39 name = "oldest", 19.40 label = _"Oldest", 19.41 - order_by = "initiative.created" 19.42 + order_by = "initiative.created, initiative.id" 19.43 } 19.44 19.45 local name = "initiative_list" 19.46 @@ -61,18 +61,22 @@ 19.47 } 19.48 columns[#columns+1] = { 19.49 content = function(record) 19.50 - if record.issue.accepted and record.issue.closed and record.issue.ranks_available then 19.51 - if record.negative_votes and record.positive_votes then 19.52 - local max_value = record.issue.voter_count 19.53 - ui.bargraph{ 19.54 - max_value = max_value, 19.55 - width = 100, 19.56 - bars = { 19.57 - { color = "#0a0", value = record.positive_votes }, 19.58 - { color = "#aaa", value = max_value - record.negative_votes - record.positive_votes }, 19.59 - { color = "#a00", value = record.negative_votes }, 19.60 + if record.issue.accepted and record.issue.closed then 19.61 + if record.issue.ranks_available then 19.62 + if record.negative_votes and record.positive_votes then 19.63 + local max_value = record.issue.voter_count 19.64 + ui.bargraph{ 19.65 + max_value = max_value, 19.66 + width = 100, 19.67 + bars = { 19.68 + { color = "#0a0", value = record.positive_votes }, 19.69 + { color = "#aaa", value = max_value - record.negative_votes - record.positive_votes }, 19.70 + { color = "#a00", value = record.negative_votes }, 19.71 + } 19.72 } 19.73 - } 19.74 + end 19.75 + else 19.76 + slot.put(_"Counting of votes") 19.77 end 19.78 else 19.79 local max_value = (record.issue.population or 0) 19.80 @@ -81,7 +85,7 @@ 19.81 width = 100, 19.82 bars = { 19.83 { color = "#0a0", value = (record.satisfied_supporter_count or 0) }, 19.84 - { color = "#8f8", value = (record.supporter_count or 0) - (record.satisfied_supporter_count or 0) }, 19.85 + { color = "#777", value = (record.supporter_count or 0) - (record.satisfied_supporter_count or 0) }, 19.86 { color = "#ddd", value = max_value - (record.supporter_count or 0) }, 19.87 } 19.88 }
20.1 --- a/app/main/initiative/new.lua Thu Dec 10 12:00:00 2009 +0100 20.2 +++ b/app/main/initiative/new.lua Fri Dec 25 12:00:00 2009 +0100 20.3 @@ -33,7 +33,7 @@ 20.4 ui.field.select{ 20.5 label = _"Policy", 20.6 name = "policy_id", 20.7 - foreign_records = Policy:new_selector():exec(), 20.8 + foreign_records = Policy:new_selector():add_order_by("index"):exec(), 20.9 foreign_id = "id", 20.10 foreign_name = "name" 20.11 }
21.1 --- a/app/main/initiative/show.lua Thu Dec 10 12:00:00 2009 +0100 21.2 +++ b/app/main/initiative/show.lua Fri Dec 25 12:00:00 2009 +0100 21.3 @@ -1,6 +1,16 @@ 21.4 local initiative = Initiative:new_selector():add_where{ "id = ?", param.get_id()}:single_object_mode():exec() 21.5 21.6 - 21.7 +slot.select("actions", function() 21.8 + ui.link{ 21.9 + content = function() 21.10 + ui.image{ static = "icons/16/script.png" } 21.11 + slot.put(_"Show all initiatives") 21.12 + end, 21.13 + module = "issue", 21.14 + view = "show", 21.15 + id = initiative.issue.id 21.16 + } 21.17 +end) 21.18 21.19 execute.view{ 21.20 module = "issue", 21.21 @@ -18,55 +28,11 @@ 21.22 params = { initiative = initiative } 21.23 } 21.24 21.25 ---[[ 21.26 - 21.27 -execute.view{ 21.28 - module = "delegation", 21.29 - view = "_show_box", 21.30 - params = { issue_id = initiative.issue_id } 21.31 -} 21.32 - 21.33 -execute.view{ 21.34 - module = "issue", 21.35 - view = "_show_box", 21.36 - params = { issue = initiative.issue } 21.37 -} 21.38 - 21.39 - 21.40 -slot.select("path", function() 21.41 - ui.link{ 21.42 - content = _"Area '#{name}'":gsub("#{name}", initiative.issue.area.name), 21.43 - module = "area", 21.44 - view = "show", 21.45 - id = initiative.issue.area.id 21.46 - } 21.47 - ui.container{ content = "::" } 21.48 - ui.link{ 21.49 - content = _"Issue ##{id}":gsub("#{id}", initiative.issue.id), 21.50 - module = "issue", 21.51 - view = "show", 21.52 - id = initiative.issue.id 21.53 - } 21.54 -end) 21.55 - 21.56 -slot.put_into("title", encode.html(_"Initiative: '#{name}'":gsub("#{name}", initiative.shortened_name) )) 21.57 ---]] 21.58 - 21.59 slot.put_into("sub_title", encode.html(_"Initiative: '#{name}'":gsub("#{name}", initiative.shortened_name) )) 21.60 21.61 slot.select("actions", function() 21.62 - 21.63 if not initiative.issue.fully_frozen and not initiative.issue.closed then 21.64 ui.link{ 21.65 - content = function() 21.66 - ui.image{ static = "icons/16/script.png" } 21.67 - slot.put(_"Show other initiatives") 21.68 - end, 21.69 - module = "issue", 21.70 - view = "show", 21.71 - id = initiative.issue.id 21.72 - } 21.73 - ui.link{ 21.74 attr = { class = "action" }, 21.75 content = function() 21.76 ui.image{ static = "icons/16/script_add.png" } 21.77 @@ -77,8 +43,6 @@ 21.78 params = { issue_id = initiative.issue.id } 21.79 } 21.80 end 21.81 --- ui.twitter("http://example.com/i" .. tostring(initiative.id) .. " " .. initiative.name) 21.82 - 21.83 end) 21.84 21.85 21.86 @@ -99,7 +63,7 @@ 21.87 ui.link{ 21.88 attr = { 21.89 class = "actions", 21.90 - target = _"blank", 21.91 + target = "_blank", 21.92 title = initiative.discussion_url 21.93 }, 21.94 content = function() 21.95 @@ -256,7 +220,7 @@ 21.96 } 21.97 } 21.98 slot.put("<br />") 21.99 - if not initiative.issue.frozen and not initiative.issue.closed then 21.100 + if not initiative.issue.fully_frozen and not initiative.issue.closed then 21.101 ui.link{ 21.102 content = function() 21.103 ui.image{ static = "icons/16/comment_add.png" } 21.104 @@ -269,7 +233,7 @@ 21.105 end 21.106 }, 21.107 { 21.108 - name = "supporter", 21.109 + name = "satisfied_supporter", 21.110 label = _"Supporter", 21.111 content = function() 21.112 execute.view{ 21.113 @@ -282,6 +246,26 @@ 21.114 :join("direct_interest_snapshot", nil, "direct_interest_snapshot.event = issue.latest_snapshot_event AND direct_interest_snapshot.issue_id = issue.id AND direct_interest_snapshot.member_id = member.id") 21.115 :add_field("direct_interest_snapshot.weight") 21.116 :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event") 21.117 + :add_where("direct_supporter_snapshot.satisfied") 21.118 + } 21.119 + } 21.120 + end 21.121 + }, 21.122 + { 21.123 + name = "supporter", 21.124 + label = _"Potential supporter", 21.125 + content = function() 21.126 + execute.view{ 21.127 + module = "member", 21.128 + view = "_list", 21.129 + params = { 21.130 + initiative = initiative, 21.131 + members_selector = initiative:get_reference_selector("supporting_members_snapshot") 21.132 + :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id") 21.133 + :join("direct_interest_snapshot", nil, "direct_interest_snapshot.event = issue.latest_snapshot_event AND direct_interest_snapshot.issue_id = issue.id AND direct_interest_snapshot.member_id = member.id") 21.134 + :add_field("direct_interest_snapshot.weight") 21.135 + :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event") 21.136 + :add_where("NOT direct_supporter_snapshot.satisfied") 21.137 } 21.138 } 21.139 end
22.1 --- a/app/main/interest/_action/update.lua Thu Dec 10 12:00:00 2009 +0100 22.2 +++ b/app/main/interest/_action/update.lua Fri Dec 25 12:00:00 2009 +0100 22.3 @@ -1,6 +1,18 @@ 22.4 local issue_id = assert(param.get("issue_id", atom.integer), "no issue id given") 22.5 + 22.6 local interest = Interest:by_pk(issue_id, app.session.member.id) 22.7 22.8 +-- TODO important m1 selectors returning result _SET_! 22.9 +local issue = interest:get_reference_selector("issue"):for_share():single_object_mode():exec() 22.10 + 22.11 +if issue.closed then 22.12 + slot.put_into("error", _"This issue is already closed.") 22.13 + return false 22.14 +elseif issue.fully_frozen then 22.15 + slot.put_into("error", _"Voting for this issue has already begun.") 22.16 + return false 22.17 +end 22.18 + 22.19 if param.get("delete", atom.boolean) then 22.20 if interest then 22.21 interest:destroy()
23.1 --- a/app/main/interest/_show_box.lua Thu Dec 10 12:00:00 2009 +0100 23.2 +++ b/app/main/interest/_show_box.lua Fri Dec 25 12:00:00 2009 +0100 23.3 @@ -15,6 +15,9 @@ 23.4 onclick = "document.getElementById('interest_content').style.display = 'block';" 23.5 }, 23.6 content = function() 23.7 + ui.image{ 23.8 + static = "icons/16/eye.png" 23.9 + } 23.10 slot.put(_"Your are interested") 23.11 ui.image{ 23.12 static = "icons/16/dropdown.png" 23.13 @@ -35,33 +38,39 @@ 23.14 ui.image{ static = "icons/16/cross.png" } 23.15 end 23.16 } 23.17 - ui.link{ 23.18 - content = _"Remove my interest", 23.19 - module = "interest", 23.20 - action = "update", 23.21 - params = { issue_id = issue.id, delete = true }, 23.22 - routing = { default = { mode = "redirect", module = "issue", view = "show", id = issue.id } } 23.23 - } 23.24 - slot.put("<br />") 23.25 - slot.put("<br />") 23.26 + if issue.state ~= "finished" and issue.state ~= "cancelled" and issue.state ~= "voting" then 23.27 + ui.link{ 23.28 + content = _"Remove my interest", 23.29 + module = "interest", 23.30 + action = "update", 23.31 + params = { issue_id = issue.id, delete = true }, 23.32 + routing = { default = { mode = "redirect", module = "issue", view = "show", id = issue.id } } 23.33 + } 23.34 + slot.put("<br />") 23.35 + slot.put("<br />") 23.36 + end 23.37 if interest.autoreject then 23.38 ui.field.text{ value = _"Autoreject is on." } 23.39 - ui.link{ 23.40 - content = _"Remove autoreject", 23.41 - module = "interest", 23.42 - action = "update", 23.43 - params = { issue_id = issue.id, autoreject = false }, 23.44 - routing = { default = { mode = "redirect", module = "issue", view = "show", id = issue.id } } 23.45 - } 23.46 + if issue.state ~= "finished" and issue.state ~= "cancelled" then 23.47 + ui.link{ 23.48 + content = _"Remove autoreject", 23.49 + module = "interest", 23.50 + action = "update", 23.51 + params = { issue_id = issue.id, autoreject = false }, 23.52 + routing = { default = { mode = "redirect", module = "issue", view = "show", id = issue.id } } 23.53 + } 23.54 + end 23.55 else 23.56 ui.field.text{ value = _"Autoreject is off." } 23.57 - ui.link{ 23.58 - content = _"Set autoreject", 23.59 - module = "interest", 23.60 - action = "update", 23.61 - params = { issue_id = issue.id, autoreject = true }, 23.62 - routing = { default = { mode = "redirect", module = "issue", view = "show", id = issue.id } } 23.63 - } 23.64 + if issue.state ~= "finished" and issue.state ~= "cancelled" then 23.65 + ui.link{ 23.66 + content = _"Set autoreject", 23.67 + module = "interest", 23.68 + action = "update", 23.69 + params = { issue_id = issue.id, autoreject = true }, 23.70 + routing = { default = { mode = "redirect", module = "issue", view = "show", id = issue.id } } 23.71 + } 23.72 + end 23.73 end 23.74 end 23.75 }
24.1 --- a/app/main/issue/_list.lua Thu Dec 10 12:00:00 2009 +0100 24.2 +++ b/app/main/issue/_list.lua Fri Dec 25 12:00:00 2009 +0100 24.3 @@ -1,7 +1,12 @@ 24.4 local issues_selector = param.get("issues_selector", "table") 24.5 24.6 +local ui_filter = ui.filter 24.7 +if param.get("filter", atom.boolean) == false then 24.8 + ui_filter = function(args) args.content() end 24.9 +end 24.10 24.11 -ui.filter{ 24.12 +local filter_voting = false 24.13 +ui_filter{ 24.14 selector = issues_selector, 24.15 filters = { 24.16 { 24.17 @@ -36,7 +41,7 @@ 24.18 label = _"Frozen", 24.19 selector_modifier = function(selector, value) 24.20 if value then 24.21 - selector:add_where("issue.half_frozen NOTNULL AND issue.closed ISNULL") 24.22 + selector:add_where("issue.half_frozen NOTNULL AND issue.fully_frozen ISNULL") 24.23 end 24.24 end 24.25 }, 24.26 @@ -47,6 +52,7 @@ 24.27 selector_modifier = function(selector, value) 24.28 if value then 24.29 selector:add_where("issue.fully_frozen NOTNULL AND issue.closed ISNULL") 24.30 + filter_voting = true 24.31 end 24.32 end 24.33 }, 24.34 @@ -56,7 +62,7 @@ 24.35 label = _"Finished", 24.36 selector_modifier = function(selector, value) 24.37 if value then 24.38 - selector:add_where("issue.closed NOTNULL AND ranks_available") 24.39 + selector:add_where("issue.closed NOTNULL AND issue.fully_frozen NOTNULL") 24.40 end 24.41 end 24.42 }, 24.43 @@ -66,98 +72,203 @@ 24.44 label = _"Cancelled", 24.45 selector_modifier = function(selector, value) 24.46 if value then 24.47 - selector:add_where("issue.closed NOTNULL AND NOT ranks_available") 24.48 + selector:add_where("issue.closed NOTNULL AND issue.accepted ISNULL") 24.49 end 24.50 end 24.51 }, 24.52 }, 24.53 content = function() 24.54 - ui.order{ 24.55 - name = "issue_list", 24.56 + local ui_filter = ui.filter 24.57 + if not filter_voting then 24.58 + ui_filter = function(args) args.content() end 24.59 + end 24.60 + ui_filter{ 24.61 selector = issues_selector, 24.62 - options = { 24.63 + name = "filter_voting", 24.64 + filters = { 24.65 { 24.66 - name = "population", 24.67 - label = _"Population", 24.68 - order_by = "issue.population DESC" 24.69 + type = "boolean", 24.70 + name = "any", 24.71 + label = _"Any", 24.72 + selector_modifier = function() end 24.73 }, 24.74 { 24.75 - name = "newest", 24.76 - label = _"Newest", 24.77 - order_by = "issue.created DESC" 24.78 + type = "boolean", 24.79 + name = "not_voted", 24.80 + label = _"Not voted", 24.81 + selector_modifier = function(selector, value) 24.82 + if value then 24.83 + selector:left_join("direct_voter", nil, { "direct_voter.issue_id = issue.id AND direct_voter.member_id = ?", app.session.member.id }) 24.84 + selector:add_where("direct_voter.member_id ISNULL") 24.85 + end 24.86 + end 24.87 }, 24.88 { 24.89 - name = "oldest", 24.90 - label = _"Oldest", 24.91 - order_by = "issue.created" 24.92 - } 24.93 + type = "boolean", 24.94 + name = "voted", 24.95 + label = _"Voted", 24.96 + selector_modifier = function(selector, value) 24.97 + if value then 24.98 + selector:join("direct_voter", nil, { "direct_voter.issue_id = issue.id AND direct_voter.member_id = ?", app.session.member.id }) 24.99 + end 24.100 + end 24.101 + }, 24.102 }, 24.103 content = function() 24.104 - ui.paginate{ 24.105 + local ui_filter = ui.filter 24.106 + if param.get("filter", atom.boolean) == false then 24.107 + ui_filter = function(args) args.content() end 24.108 + end 24.109 + ui_filter{ 24.110 selector = issues_selector, 24.111 + name = "filter_interest", 24.112 + filters = { 24.113 + { 24.114 + type = "boolean", 24.115 + name = "any", 24.116 + label = _"Any", 24.117 + selector_modifier = function() end 24.118 + }, 24.119 + { 24.120 + type = "boolean", 24.121 + name = "my", 24.122 + label = _"Interested", 24.123 + selector_modifier = function(selector, value) 24.124 + if value then 24.125 + selector:join("interest", "filter_interest", { "filter_interest.issue_id = issue.id AND filter_interest.member_id = ? ", app.session.member.id }) 24.126 + end 24.127 + end 24.128 + }, 24.129 + }, 24.130 content = function() 24.131 - local highlight_string = param.get("highlight_string", "string") 24.132 - local issues = issues or issues_selector:exec() 24.133 --- issues:load(initiatives) 24.134 - ui.list{ 24.135 - attr = { class = "issues" }, 24.136 - records = issues, 24.137 - columns = { 24.138 + 24.139 + ui.order{ 24.140 + name = "issue_list", 24.141 + selector = issues_selector, 24.142 + options = { 24.143 { 24.144 - label = _"Issue", 24.145 - content = function(record) 24.146 - if not param.get("for_area_list", atom.boolean) then 24.147 - ui.field.text{ 24.148 - value = record.area.name 24.149 - } 24.150 - slot.put("<br />") 24.151 - end 24.152 - ui.link{ 24.153 - text = _"Issue ##{id}":gsub("#{id}", tostring(record.id)), 24.154 - module = "issue", 24.155 - view = "show", 24.156 - id = record.id 24.157 - } 24.158 - if record.state == "new" then 24.159 - ui.image{ 24.160 - static = "icons/16/new.png" 24.161 - } 24.162 - end 24.163 - slot.put("<br />") 24.164 - slot.put("<br />") 24.165 + name = "max_potential_support", 24.166 + label = _"Max potential support", 24.167 + selector_modifier = function(selector) 24.168 + selector:add_order_by("(SELECT max(supporter_count) FROM initiative WHERE initiative.issue_id = issue.id)") 24.169 end 24.170 }, 24.171 { 24.172 - label = _"State", 24.173 - content = function(record) 24.174 - ui.field.issue_state{ value = record.state } 24.175 + name = "max_support", 24.176 + label = _"Max support", 24.177 + selector_modifier = function(selector) 24.178 + selector:add_order_by("(SELECT max(satisfied_supporter_count) FROM initiative WHERE initiative.issue_id = issue.id)") 24.179 end 24.180 }, 24.181 { 24.182 - label = _"Initiatives", 24.183 - content = function(record) 24.184 - local initiatives_selector = record:get_reference_selector("initiatives") 24.185 - local highlight_string = param.get("highlight_string") 24.186 - if highlight_string then 24.187 - initiatives_selector:add_field( {'"highlight"("initiative"."name", ?)', highlight_string }, "name_highlighted") 24.188 - end 24.189 - execute.view{ 24.190 - module = "initiative", 24.191 - view = "_list", 24.192 - params = { 24.193 - issue = record, 24.194 - initiatives_selector = initiatives_selector, 24.195 - highlight_string = highlight_string, 24.196 - limit = 3 24.197 + name = "population", 24.198 + label = _"Population", 24.199 + order_by = "issue.population DESC" 24.200 + }, 24.201 + { 24.202 + name = "newest", 24.203 + label = _"Newest", 24.204 + order_by = "issue.created DESC" 24.205 + }, 24.206 + { 24.207 + name = "oldest", 24.208 + label = _"Oldest", 24.209 + order_by = "issue.created" 24.210 + } 24.211 + }, 24.212 + content = function() 24.213 + ui.paginate{ 24.214 + selector = issues_selector, 24.215 + content = function() 24.216 + local highlight_string = param.get("highlight_string", "string") 24.217 + local issues = issues or issues_selector:exec() 24.218 + -- issues:load(initiatives) 24.219 + ui.list{ 24.220 + attr = { class = "issues" }, 24.221 + records = issues, 24.222 + columns = { 24.223 + { 24.224 + label = _"Issue", 24.225 + content = function(record) 24.226 + if not param.get("for_area_list", atom.boolean) then 24.227 + ui.field.text{ 24.228 + value = record.area.name 24.229 + } 24.230 + slot.put("<br />") 24.231 + end 24.232 + ui.link{ 24.233 + text = _"Issue ##{id}":gsub("#{id}", tostring(record.id)), 24.234 + module = "issue", 24.235 + view = "show", 24.236 + id = record.id 24.237 + } 24.238 + if record.state == "new" then 24.239 + ui.image{ 24.240 + static = "icons/16/new.png" 24.241 + } 24.242 + end 24.243 + slot.put("<br />") 24.244 + slot.put("<br />") 24.245 + end 24.246 + }, 24.247 + { 24.248 + label = _"State", 24.249 + content = function(record) 24.250 + ui.field.issue_state{ value = record.state } 24.251 + end 24.252 + }, 24.253 + { 24.254 + label = _"Initiatives", 24.255 + content = function(record) 24.256 + local initiatives_selector = record:get_reference_selector("initiatives") 24.257 + local highlight_string = param.get("highlight_string") 24.258 + if highlight_string then 24.259 + initiatives_selector:add_field( {'"highlight"("initiative"."name", ?)', highlight_string }, "name_highlighted") 24.260 + end 24.261 + execute.view{ 24.262 + module = "initiative", 24.263 + view = "_list", 24.264 + params = { 24.265 + issue = record, 24.266 + initiatives_selector = initiatives_selector, 24.267 + highlight_string = highlight_string, 24.268 + limit = 3 24.269 + } 24.270 + } 24.271 + end 24.272 + }, 24.273 } 24.274 } 24.275 end 24.276 - }, 24.277 - } 24.278 + } 24.279 + end 24.280 } 24.281 end 24.282 } 24.283 end 24.284 } 24.285 + if param.get("legend", atom.boolean) ~= false then 24.286 + local filter = param.get_all_cgi().filter 24.287 + if not filter or filter == "any" or filter ~= "finished" then 24.288 + ui.bargraph_legend{ 24.289 + width = 25, 24.290 + bars = { 24.291 + { color = "#0a0", label = _"Supporter" }, 24.292 + { color = "#777", label = _"Potential supporter" }, 24.293 + { color = "#ddd", label = _"No support at all" }, 24.294 + } 24.295 + } 24.296 + end 24.297 + if not filter or filter == "any" or filter == "finished" then 24.298 + ui.bargraph_legend{ 24.299 + width = 25, 24.300 + bars = { 24.301 + { color = "#0a0", label = _"Yes" }, 24.302 + { color = "#aaa", label = _"Abstention" }, 24.303 + { color = "#a00", label = _"No" }, 24.304 + } 24.305 + } 24.306 + end 24.307 + end 24.308 end 24.309 }
25.1 --- a/app/main/issue/_show_head.lua Thu Dec 10 12:00:00 2009 +0100 25.2 +++ b/app/main/issue/_show_head.lua Fri Dec 25 12:00:00 2009 +0100 25.3 @@ -11,21 +11,35 @@ 25.4 } 25.5 end) 25.6 25.7 -slot.put_into("title", encode.html(_"Issue ##{id} (#{policy_name})":gsub("#{id}", issue.id):gsub("#{policy_name}", issue.policy.name))) 25.8 +slot.select("title", function() 25.9 + ui.link{ 25.10 + content = _"Issue ##{id} (#{policy_name})":gsub("#{id}", issue.id):gsub("#{policy_name}", issue.policy.name), 25.11 + module = "issue", 25.12 + view = "show", 25.13 + id = issue.id 25.14 + } 25.15 +end) 25.16 + 25.17 25.18 slot.select("actions", function() 25.19 + 25.20 + if issue.state == 'voting' then 25.21 + ui.link{ 25.22 + content = function() 25.23 + ui.image{ static = "icons/16/email_open.png" } 25.24 + slot.put(_"Vote now") 25.25 + end, 25.26 + module = "vote", 25.27 + view = "list", 25.28 + params = { issue_id = issue.id } 25.29 + } 25.30 + end 25.31 + 25.32 execute.view{ 25.33 module = "interest", 25.34 view = "_show_box", 25.35 params = { issue = issue } 25.36 } 25.37 - 25.38 - execute.view{ 25.39 - module = "delegation", 25.40 - view = "_show_box", 25.41 - params = { issue_id = issue.id } 25.42 - } 25.43 - 25.44 -- TODO performance 25.45 local interest = Interest:by_pk(issue.id, app.session.member.id) 25.46 if not issue.closed and not issue.fully_frozen then 25.47 @@ -43,6 +57,23 @@ 25.48 end 25.49 end 25.50 25.51 + if not issue.closed then 25.52 + execute.view{ 25.53 + module = "delegation", 25.54 + view = "_show_box", 25.55 + params = { issue_id = issue.id } 25.56 + } 25.57 + end 25.58 + 25.59 + if issue.state == "accepted" then 25.60 + -- TODO 25.61 + ui.link{ 25.62 + content = function() 25.63 + ui.image{ static = "icons/16/time.png" } 25.64 + slot.put(_"Vote now/later") 25.65 + end, 25.66 + } 25.67 + end 25.68 25.69 end) 25.70
26.1 --- a/app/main/issue/show.lua Thu Dec 10 12:00:00 2009 +0100 26.2 +++ b/app/main/issue/show.lua Fri Dec 25 12:00:00 2009 +0100 26.3 @@ -12,7 +12,8 @@ 26.4 { 26.5 name = "initiatives", 26.6 label = _"Initiatives", 26.7 - content = function() execute.view{ 26.8 + content = function() 26.9 + execute.view{ 26.10 module = "initiative", 26.11 view = "_list", 26.12 params = {
27.1 --- a/app/main/member/_action/update.lua Thu Dec 10 12:00:00 2009 +0100 27.2 +++ b/app/main/member/_action/update.lua Fri Dec 25 12:00:00 2009 +0100 27.3 @@ -1,5 +1,4 @@ 27.4 param.update(app.session.member, 27.5 - "name", 27.6 "organizational_unit", 27.7 "internal_posts", 27.8 "realname",
28.1 --- a/app/main/member/edit.lua Thu Dec 10 12:00:00 2009 +0100 28.2 +++ b/app/main/member/edit.lua Fri Dec 25 12:00:00 2009 +0100 28.3 @@ -26,7 +26,6 @@ 28.4 } 28.5 }, 28.6 content = function() 28.7 - ui.field.text{ label = _"Name", name = "name" } 28.8 ui.field.text{ label = _"Organizational unit", name = "organizational_unit" } 28.9 ui.field.text{ label = _"Internal posts", name = "internal_posts" } 28.10 ui.field.text{ label = _"Real name", name = "realname" }
29.1 --- a/app/main/opinion/_action/update.lua Thu Dec 10 12:00:00 2009 +0100 29.2 +++ b/app/main/opinion/_action/update.lua Fri Dec 25 12:00:00 2009 +0100 29.3 @@ -4,6 +4,19 @@ 29.4 29.5 local opinion = Opinion:by_pk(member_id, suggestion_id) 29.6 29.7 +-- TODO important m1 selectors returning result _SET_! 29.8 +local issue = opinion.initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() 29.9 + 29.10 +if issue.closed then 29.11 + slot.put_into("error", _"This issue is already closed.") 29.12 + return false 29.13 +elseif issue.fully_frozen then 29.14 + slot.put_into("error", _"Voting for this issue has already begun.") 29.15 + return false 29.16 +end 29.17 + 29.18 + 29.19 + 29.20 if param.get("delete") then 29.21 if opinion then 29.22 opinion:destroy()
30.1 --- a/app/main/suggestion/_action/add.lua Thu Dec 10 12:00:00 2009 +0100 30.2 +++ b/app/main/suggestion/_action/add.lua Fri Dec 25 12:00:00 2009 +0100 30.3 @@ -1,4 +1,8 @@ 30.4 -db:query("BEGIN") 30.5 +local tmp = db:query({ "SELECT text_entries_left FROM member_contingent_left WHERE member_id = ?", app.session.member.id }, "opt_object") 30.6 +if tmp and tmp.text_entries_left and tmp.text_entries_left < 1 then 30.7 + slot.put_into("error", _"Sorry, you have reached your personal flood limit. Please be slower...") 30.8 + return false 30.9 +end 30.10 30.11 local suggestion = Suggestion:new() 30.12 30.13 @@ -6,6 +10,17 @@ 30.14 param.update(suggestion, "name", "description", "initiative_id") 30.15 suggestion:save() 30.16 30.17 +-- TODO important m1 selectors returning result _SET_! 30.18 +local issue = suggestion.initiative:get_reference_selector("issue"):for_share():single_object_mode():exec() 30.19 + 30.20 +if issue.closed then 30.21 + slot.put_into("error", _"This issue is already closed.") 30.22 + return false 30.23 +elseif issue.fully_frozen then 30.24 + slot.put_into("error", _"Voting for this issue has already begun.") 30.25 + return false 30.26 +end 30.27 + 30.28 local opinion = Opinion:new() 30.29 30.30 opinion.suggestion_id = suggestion.id 30.31 @@ -15,6 +30,4 @@ 30.32 30.33 opinion:save() 30.34 30.35 -db:query("COMMIT") 30.36 - 30.37 slot.put_into("notice", _"Your suggestion has been added") 30.38 \ No newline at end of file
31.1 --- a/app/main/suggestion/_list.lua Thu Dec 10 12:00:00 2009 +0100 31.2 +++ b/app/main/suggestion/_list.lua Fri Dec 25 12:00:00 2009 +0100 31.3 @@ -105,60 +105,69 @@ 31.4 if opinion then 31.5 degree = opinion.degree 31.6 end 31.7 - ui.link{ 31.8 - attr = { class = "action" .. (degree == -2 and " active_red2" or "") }, 31.9 - text = _"must not", 31.10 - module = "opinion", 31.11 - action = "update", 31.12 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 31.13 - params = { 31.14 - suggestion_id = record.id, 31.15 - degree = -2 31.16 - } 31.17 - } 31.18 - ui.link{ 31.19 - attr = { class = "action" .. (degree == -1 and " active_red1" or "") }, 31.20 - text = _"should not", 31.21 - module = "opinion", 31.22 - action = "update", 31.23 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 31.24 - params = { 31.25 - suggestion_id = record.id, 31.26 - degree = -1 31.27 - } 31.28 - } 31.29 - ui.link{ 31.30 - attr = { class = "action" .. (degree == nil and " active" or "") }, 31.31 - text = _"neutral", 31.32 - module = "opinion", 31.33 - action = "update", 31.34 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 31.35 - params = { 31.36 - suggestion_id = record.id, 31.37 - delete = true 31.38 - } 31.39 - } 31.40 - ui.link{ 31.41 - attr = { class = "action" .. (degree == 1 and " active_green1" or "") }, 31.42 - text = _"should", 31.43 - module = "opinion", 31.44 - action = "update", 31.45 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 31.46 - params = { 31.47 - suggestion_id = record.id, 31.48 - degree = 1 31.49 - } 31.50 - } 31.51 - ui.link{ 31.52 - attr = { class = "action" .. (degree == 2 and " active_green2" or "") }, 31.53 - text = _"must", 31.54 - module = "opinion", 31.55 - action = "update", 31.56 - routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 31.57 - params = { 31.58 - suggestion_id = record.id, 31.59 - degree = 2 31.60 - } 31.61 + ui.container{ 31.62 + attr = { class = "suggestion_my_opinion" }, 31.63 + content = function() 31.64 + ui.link{ 31.65 + attr = { class = "action" .. (degree == -2 and " active_red2" or "") }, 31.66 + text = _"must not", 31.67 + module = "opinion", 31.68 + action = "update", 31.69 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 31.70 + params = { 31.71 + suggestion_id = record.id, 31.72 + degree = -2 31.73 + } 31.74 + } 31.75 + slot.put(" ") 31.76 + ui.link{ 31.77 + attr = { class = "action" .. (degree == -1 and " active_red1" or "") }, 31.78 + text = _"should not", 31.79 + module = "opinion", 31.80 + action = "update", 31.81 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 31.82 + params = { 31.83 + suggestion_id = record.id, 31.84 + degree = -1 31.85 + } 31.86 + } 31.87 + slot.put(" ") 31.88 + ui.link{ 31.89 + attr = { class = "action" .. (degree == nil and " active" or "") }, 31.90 + text = _"neutral", 31.91 + module = "opinion", 31.92 + action = "update", 31.93 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 31.94 + params = { 31.95 + suggestion_id = record.id, 31.96 + delete = true 31.97 + } 31.98 + } 31.99 + slot.put(" ") 31.100 + ui.link{ 31.101 + attr = { class = "action" .. (degree == 1 and " active_green1" or "") }, 31.102 + text = _"should", 31.103 + module = "opinion", 31.104 + action = "update", 31.105 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 31.106 + params = { 31.107 + suggestion_id = record.id, 31.108 + degree = 1 31.109 + } 31.110 + } 31.111 + slot.put(" ") 31.112 + ui.link{ 31.113 + attr = { class = "action" .. (degree == 2 and " active_green2" or "") }, 31.114 + text = _"must", 31.115 + module = "opinion", 31.116 + action = "update", 31.117 + routing = { default = { mode = "redirect", module = request.get_module(), view = request.get_view(), id = param.get_id_cgi(), params = param.get_all_cgi() } }, 31.118 + params = { 31.119 + suggestion_id = record.id, 31.120 + degree = 2 31.121 + } 31.122 + } 31.123 + end 31.124 } 31.125 end 31.126 },
32.1 --- a/app/main/supporter/_show_box.lua Thu Dec 10 12:00:00 2009 +0100 32.2 +++ b/app/main/supporter/_show_box.lua Fri Dec 25 12:00:00 2009 +0100 32.3 @@ -1,29 +1,48 @@ 32.4 32.5 slot.select("support", function() 32.6 local initiative = param.get("initiative", "table") 32.7 - local supported = Supporter:by_pk(initiative.id, app.session.member.id) and true or false 32.8 + local supporter = Supporter:by_pk(initiative.id, app.session.member.id) 32.9 32.10 ui.container{ 32.11 attr = { class = "actions" }, 32.12 content = function() 32.13 - if not initiative.issue.frozen and not initiative.issue.closed then 32.14 - if supported then 32.15 - ui.container{ 32.16 - attr = { 32.17 - class = "head head_active", 32.18 - style = "cursor: pointer;", 32.19 - onclick = "document.getElementById('support_content').style.display = 'block';" 32.20 - }, 32.21 - content = function() 32.22 - ui.image{ 32.23 - static = "icons/16/thumb_up_green.png" 32.24 - } 32.25 - slot.put(_"Your are supporter") 32.26 - ui.image{ 32.27 - static = "icons/16/dropdown.png" 32.28 - } 32.29 - end 32.30 - } 32.31 + if not initiative.issue.fully_frozen and not initiative.issue.closed then 32.32 + if supporter then 32.33 + if not supporter:has_critical_opinion() then 32.34 + ui.container{ 32.35 + attr = { 32.36 + class = "head head_supporter", 32.37 + style = "cursor: pointer;", 32.38 + onclick = "document.getElementById('support_content').style.display = 'block';" 32.39 + }, 32.40 + content = function() 32.41 + ui.image{ 32.42 + static = "icons/16/thumb_up_green.png" 32.43 + } 32.44 + slot.put(_"Your are supporter") 32.45 + ui.image{ 32.46 + static = "icons/16/dropdown.png" 32.47 + } 32.48 + end 32.49 + } 32.50 + else 32.51 + ui.container{ 32.52 + attr = { 32.53 + class = "head head_potential_supporter", 32.54 + style = "cursor: pointer;", 32.55 + onclick = "document.getElementById('support_content').style.display = 'block';" 32.56 + }, 32.57 + content = function() 32.58 + ui.image{ 32.59 + static = "icons/16/thumb_up.png" 32.60 + } 32.61 + slot.put(_"Your are potential supporter") 32.62 + ui.image{ 32.63 + static = "icons/16/dropdown.png" 32.64 + } 32.65 + end 32.66 + } 32.67 + end 32.68 ui.container{ 32.69 attr = { class = "content", id = "support_content" }, 32.70 content = function() 32.71 @@ -37,7 +56,7 @@ 32.72 ui.image{ static = "icons/16/cross.png" } 32.73 end 32.74 } 32.75 - if supported then 32.76 + if supporter then 32.77 ui.link{ 32.78 content = function() 32.79 ui.image{ static = "icons/16/thumb_down_red.png" }
33.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 33.2 +++ b/app/main/vote/_action/update.lua Fri Dec 25 12:00:00 2009 +0100 33.3 @@ -0,0 +1,47 @@ 33.4 +local issue = Issue:new_selector():add_where{ "id = ?", param.get("issue_id", atom.integer) }:for_share():single_object_mode():exec() 33.5 + 33.6 +if issue.closed then 33.7 + slot.put_into("error", _"This issue is already closed.") 33.8 + return false 33.9 +end 33.10 + 33.11 +if issue.state ~= "voting" then 33.12 + slot.put_into("error", _"Voting has not started yet.") 33.13 + return false 33.14 +end 33.15 + 33.16 +local direct_voter = DirectVoter:by_pk(issue.id, app.session.member_id) 33.17 + 33.18 +if not direct_voter then 33.19 + direct_voter = DirectVoter:new() 33.20 + direct_voter.issue_id = issue.id 33.21 + direct_voter.member_id = app.session.member_id 33.22 +end 33.23 + 33.24 +direct_voter.autoreject = false 33.25 + 33.26 +direct_voter:save() 33.27 + 33.28 + 33.29 +local scoring = param.get("scoring") 33.30 + 33.31 +for initiative_id, grade in scoring:gmatch("([^:;]+):([^:;]+)") do 33.32 + local initiative_id = tonumber(initiative_id) 33.33 + local grade = tonumber(grade) 33.34 + local initiative = Initiative:by_id(initiative_id) 33.35 + if initiative.issue.id ~= issue.id then 33.36 + error("initiative from wrong issue") 33.37 + end 33.38 + local vote = Vote:by_pk(initiative_id, app.session.member.id) 33.39 + if not vote then 33.40 + vote = Vote:new() 33.41 + vote.issue_id = issue.id 33.42 + vote.initiative_id = initiative.id 33.43 + vote.member_id = app.session.member.id 33.44 + end 33.45 + vote.grade = grade 33.46 + vote:save() 33.47 +end 33.48 + 33.49 +trace.debug(scoring) 33.50 +
34.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 34.2 +++ b/app/main/vote/list.lua Fri Dec 25 12:00:00 2009 +0100 34.3 @@ -0,0 +1,138 @@ 34.4 +local warning_text = _"Some JavaScript based functions (voting in particular) will not work.\nFor this beta, please use a current version of Firefox, Safari, Opera(?), Konqueror or another (more) standard compliant browser.\nAlternative access without JavaScript will be available soon." 34.5 + 34.6 +ui.script{ static = "js/browser_warning.js" } 34.7 +ui.script{ script = "checkBrowser(" .. encode.json(_"Your web browser is not fully supported yet." .. " " .. warning_text:gsub("\n", "\n\n")) .. ");" } 34.8 + 34.9 +ui.tag{ 34.10 + tag = "noscript", 34.11 + content = function() 34.12 + slot.put(_"JavaScript is disabled or not available." .. " " .. encode.html_newlines(warning_text)) 34.13 + end 34.14 +} 34.15 + 34.16 + 34.17 +local issue = Issue:by_id(param.get("issue_id"), atom.integer) 34.18 + 34.19 +local initiatives = issue.initiatives 34.20 + 34.21 +local min_grade = -1; 34.22 +local max_grade = 1; 34.23 + 34.24 +for i, initiative in ipairs(initiatives) do 34.25 + -- TODO performance 34.26 + initiative.vote = Vote:by_pk(initiative.id, app.session.member.id) 34.27 + if initiative.vote then 34.28 + if initiative.vote.grade > max_grade then 34.29 + max_grade = initiative.vote.grade 34.30 + end 34.31 + if initiative.vote.grade < min_grade then 34.32 + min_grade = initiative.vote.grade 34.33 + end 34.34 + end 34.35 +end 34.36 + 34.37 +local sections = {} 34.38 +for i = min_grade, max_grade do 34.39 + sections[i] = {} 34.40 + for j, initiative in ipairs(initiatives) do 34.41 + if (initiative.vote and initiative.vote.grade == i) or (not initiative.vote and i == 0) then 34.42 + sections[i][#(sections[i])+1] = initiative 34.43 + end 34.44 + end 34.45 +end 34.46 + 34.47 +slot.put_into("title", _"Voting") 34.48 + 34.49 +slot.select("actions", function() 34.50 + ui.link{ 34.51 + content = function() 34.52 + ui.image{ static = "icons/16/cancel.png" } 34.53 + slot.put(_"Cancel") 34.54 + end, 34.55 + module = "issue", 34.56 + view = "show", 34.57 + id = issue.id 34.58 + } 34.59 +end) 34.60 + 34.61 +util.help("vote.list", _"Voting") 34.62 + 34.63 + 34.64 +slot.put('<script src="' .. request.get_relative_baseurl() .. 'static/js/dragdrop.js"></script>') 34.65 +slot.put('<script src="' .. request.get_relative_baseurl() .. 'static/js/voting.js"></script>') 34.66 + 34.67 +ui.form{ 34.68 + attr = { id = "voting_form" }, 34.69 + module = "vote", 34.70 + action = "update", 34.71 + params = { issue_id = issue.id }, 34.72 + routing = { 34.73 + default = { 34.74 + mode = "redirect", 34.75 + module = "issue", 34.76 + view = "show", 34.77 + id = issue.id 34.78 + } 34.79 + }, 34.80 + content = function() 34.81 + slot.put('<input type="hidden" name="scoring" value=""/>') 34.82 + -- TODO abstrahieren 34.83 + ui.tag{ 34.84 + tag = "input", 34.85 + attr = { 34.86 + type = "button", 34.87 + class = "voting_done", 34.88 + value = _"Finish voting" 34.89 + } 34.90 + } 34.91 + ui.container{ 34.92 + attr = { id = "voting" }, 34.93 + content = function() 34.94 + for grade = max_grade, min_grade, -1 do 34.95 + local section = sections[grade] 34.96 + local class 34.97 + if grade > 0 then 34.98 + class = "approval" 34.99 + elseif grade < 0 then 34.100 + class = "disapproval" 34.101 + else 34.102 + class = "abstention" 34.103 + end 34.104 + ui.container{ 34.105 + attr = { class = class }, 34.106 + content = function() 34.107 + slot.put('<div class="cathead"></div>') 34.108 + for i, initiative in ipairs(section) do 34.109 + ui.container{ 34.110 + attr = { 34.111 + class = "movable", 34.112 + id = "entry_" .. tostring(initiative.id) 34.113 + }, 34.114 + content = function() 34.115 + ui.link{ 34.116 + attr = { class = "clickable" }, 34.117 + content = initiative.name, 34.118 + module = "initiative", 34.119 + view = "show", 34.120 + id = initiative.id 34.121 + } 34.122 + end 34.123 + } 34.124 + end 34.125 + end 34.126 + } 34.127 + end 34.128 + end 34.129 + } 34.130 + ui.tag{ 34.131 + tag = "input", 34.132 + attr = { 34.133 + type = "button", 34.134 + class = "voting_done", 34.135 + value = _"Finish voting" 34.136 + } 34.137 + } 34.138 + end 34.139 +} 34.140 + 34.141 +
35.1 --- a/config/default.lua Thu Dec 10 12:00:00 2009 +0100 35.2 +++ b/config/default.lua Fri Dec 25 12:00:00 2009 +0100 35.3 @@ -1,5 +1,5 @@ 35.4 config.app_name = "LiquidFeedback" 35.5 -config.app_version = "alpha5" 35.6 +config.app_version = "beta1" 35.7 35.8 config.app_title = config.app_name .. " (" .. request.get_config_name() .. " environment)" 35.9
36.1 --- a/env/ui/order.lua Thu Dec 10 12:00:00 2009 +0100 36.2 +++ b/env/ui/order.lua Fri Dec 25 12:00:00 2009 +0100 36.3 @@ -16,7 +16,11 @@ 36.4 local attr = {} 36.5 if current_order == option.name then 36.6 attr.class = "active" 36.7 - args.selector:add_order_by(option.order_by) 36.8 + if option.selector_modifier then 36.9 + option.selector_modifier(args.selector) 36.10 + else 36.11 + args.selector:add_order_by(option.order_by) 36.12 + end 36.13 end 36.14 ui.link{ 36.15 attr = attr,
37.1 --- a/fastpath/Makefile Thu Dec 10 12:00:00 2009 +0100 37.2 +++ b/fastpath/Makefile Fri Dec 25 12:00:00 2009 +0100 37.3 @@ -1,5 +1,5 @@ 37.4 getpic: getpic.c 37.5 - cc -g -Wall -o getpic getpic.c -I /usr/pkg/include/ -I /usr/include/postgresql -L /usr/pkg/lib -L /usr/lib/postgresql -lpq -Wl,-rpath,/usr/pkg/lib -Wl,-rpath,/usr/lib/postgresql 37.6 + cc -g -Wall -o getpic getpic.c -I `pg_config --includedir` -L `pg_config --libdir` -lpq -Wl,-rpath,`pg_config --libdir` 37.7 37.8 clean:: 37.9 rm -f getpic
38.1 --- a/fastpath/getpic.c Thu Dec 10 12:00:00 2009 +0100 38.2 +++ b/fastpath/getpic.c Fri Dec 25 12:00:00 2009 +0100 38.3 @@ -16,67 +16,50 @@ 38.4 #endif 38.5 38.6 int main(int argc, const char * const *argv) { 38.7 - PGconn *conn; 38.8 - PGresult *dbr; 38.9 - 38.10 - char *cookies = getenv("HTTP_COOKIE"); 38.11 38.12 char *args_string; 38.13 char *member_id; 38.14 char *image_type; 38.15 - 38.16 - char *sql_session_params[1]; 38.17 char *sql_member_image_params[2]; 38.18 38.19 + char *cookies; 38.20 + regex_t session_ident_regex; 38.21 ssize_t start, length; 38.22 - 38.23 + regmatch_t session_ident_regmatch[3]; 38.24 char *session_ident; 38.25 + char *sql_session_params[1]; 38.26 38.27 - regex_t session_ident_regex; 38.28 - regmatch_t session_ident_regmatch[2]; 38.29 - 38.30 - cookies = getenv("HTTP_COOKIE"); 38.31 + PGconn *conn; 38.32 + PGresult *dbr; 38.33 38.34 args_string = getenv("QUERY_STRING"); 38.35 - 38.36 - if (!cookies || !args_string) { 38.37 + cookies = getenv("HTTP_COOKIE"); 38.38 + if (!args_string || !cookies) { 38.39 fputs("Status: 403 Access Denied\n\n", stdout); 38.40 return 0; 38.41 } 38.42 38.43 member_id = strtok(args_string, "+"); 38.44 image_type = strtok(NULL, "+"); 38.45 - 38.46 sql_member_image_params[0] = member_id; 38.47 sql_member_image_params[1] = image_type; 38.48 38.49 - // get session from cookie 38.50 - 38.51 - // TODO improve regex to fit better 38.52 - if (regcomp(&session_ident_regex, "liquid_feedback_session=([a-zA-Z0-9]+)", REG_EXTENDED) != 0) { 38.53 + if (regcomp(&session_ident_regex, "(^|[; \t])liquid_feedback_session=([0-9A-Za-z]+)", REG_EXTENDED) != 0) { 38.54 // shouldn't happen 38.55 abort(); 38.56 } 38.57 - 38.58 if (regexec(&session_ident_regex, cookies, 2, session_ident_regmatch, 0) != 0) { 38.59 fputs("Status: 403 Access Denied\n\n", stdout); 38.60 return 0; 38.61 } 38.62 - 38.63 - start = session_ident_regmatch[1].rm_so; 38.64 - length = session_ident_regmatch[1].rm_eo - session_ident_regmatch[1].rm_so; 38.65 - 38.66 + start = session_ident_regmatch[2].rm_so; 38.67 + length = session_ident_regmatch[2].rm_eo - session_ident_regmatch[2].rm_so; 38.68 session_ident = malloc(length + 1); 38.69 - 38.70 + if (!session_ident) abort(); // shouldn't happen 38.71 strncpy(session_ident, cookies + start, length); 38.72 - 38.73 session_ident[length] = 0; 38.74 - 38.75 sql_session_params[0] = session_ident; 38.76 38.77 - 38.78 - // connect to database 38.79 - 38.80 conn = PQconnectdb(GETPIC_CONNINFO); 38.81 if (!conn) { 38.82 fputs("Could not create PGconn structure.\n", stderr); 38.83 @@ -84,27 +67,25 @@ 38.84 } 38.85 if (PQstatus(conn) != CONNECTION_OK) { 38.86 fputs(PQerrorMessage(conn), stderr); 38.87 + PQfinish(conn); 38.88 return 1; 38.89 } 38.90 38.91 - // check session 38.92 dbr = PQexecParams(conn, 38.93 "SELECT NULL FROM session JOIN member ON member.id = session.member_id WHERE session.ident = $1 AND member.active", 38.94 1, NULL, sql_session_params, NULL, NULL, 0 38.95 ); 38.96 - 38.97 if (PQresultStatus(dbr) != PGRES_TUPLES_OK) { 38.98 fputs(PQresultErrorMessage(dbr), stderr); 38.99 + PQfinish(conn); 38.100 return 1; 38.101 } 38.102 - 38.103 if (PQntuples(dbr) != 1) { 38.104 fputs("Status: 403 Access Denied\n\n", stdout); 38.105 + PQfinish(conn); 38.106 return 0; 38.107 } 38.108 38.109 - 38.110 - // get picture 38.111 dbr = PQexecParams(conn, 38.112 "SELECT content_type, data " 38.113 "FROM member_image " 38.114 @@ -114,35 +95,34 @@ 38.115 "LIMIT 1;", 38.116 2, NULL, sql_member_image_params, NULL, NULL, 1 38.117 ); 38.118 - 38.119 if (PQresultStatus(dbr) != PGRES_TUPLES_OK) { 38.120 fputs(PQresultErrorMessage(dbr), stderr); 38.121 - return 1; 38.122 - } 38.123 - if (PQntuples(dbr) > 1) { 38.124 + PQfinish(conn); 38.125 return 1; 38.126 } 38.127 - fputs("Cache-Control: private; max-age=86400\n", stdout); 38.128 if (PQntuples(dbr) == 0) { 38.129 struct stat sb; 38.130 PQclear(dbr); 38.131 PQfinish(conn); 38.132 fputs("Content-Type: image/jpeg\n\n", stdout); 38.133 if (stat(GETPIC_DEFAULT_AVATAR, &sb)) return 1; 38.134 - fprintf(stdout, "Content-Length: %i\n", sb.st_size); 38.135 + fprintf(stdout, "Content-Length: %i\n", (int)sb.st_size); 38.136 execl("/bin/cat", "cat", GETPIC_DEFAULT_AVATAR, NULL); 38.137 return 1; 38.138 } else { 38.139 if (PQnfields(dbr) < 0) { 38.140 fputs("Too few columns returned by database.\n", stderr); 38.141 + PQfinish(conn); 38.142 return 1; 38.143 } 38.144 if (PQfformat(dbr, 0) != 1 || PQfformat(dbr, 1) != 1) { 38.145 fputs("Database did not return data in binary format.\n", stderr); 38.146 + PQfinish(conn); 38.147 return 1; 38.148 } 38.149 if (PQgetisnull(dbr, 0, 0) || PQgetisnull(dbr, 0, 1)) { 38.150 fputs("Unexpected NULL in database result.\n", stderr); 38.151 + PQfinish(conn); 38.152 return 1; 38.153 } 38.154 fputs("Content-Type: ", stdout); 38.155 @@ -151,7 +131,7 @@ 38.156 fputs("\n\n", stdout); 38.157 fwrite(PQgetvalue(dbr, 0, 1), PQgetlength(dbr, 0, 1), 1, stdout); 38.158 } 38.159 - PQclear(dbr); 38.160 PQfinish(conn); 38.161 return 0; 38.162 + 38.163 }
39.1 --- a/locale/help/area.show.de.txt Thu Dec 10 12:00:00 2009 +0100 39.2 +++ b/locale/help/area.show.de.txt Fri Dec 25 12:00:00 2009 +0100 39.3 @@ -1,6 +1,8 @@ 39.4 -=Themenbereich, Mitgliedschaft= 39.5 -Für die Teilnahme an der Diskussion und Endabstimmung einzelner Themen ist die Mitgliedschaft im Themenbereich **keine** Voraussetzung. Mit der Mitgliedschaft erklärst du vielmehr ein generelles Interesse an diesem Themenbereich. 39.6 -=Delegation, Auto-Ablehnen= 39.7 -Du kannst deine Stimme für diesen Themenbereich (unabhängig von deiner Mitgliedschaft im Themenbereich) an eine Person in deiner Kontaktliste delegieren. Als Themenbereichsmitglied kannst du ,,Auto-Ablehnen'' als Standardabstimmverhalten festlegen. Diese Weisung gilt nur für den Fall, dass du nicht selbst an der Endabstimmung teilnimmst, es keine tatsächlich genutzte Delegation und keine Weisung für das jeweilige konkrete Thema gibt. 39.8 -=Neues Thema= 39.9 -Hier kannst du mit einer eigenen Initiative ein neues Thema eröffnen. **Bevor** du ein neues Thema eröffnest, solltest du prüfen, ob es dieses Thema vieleicht schon gibt. Eine Initiative zu einem bestehenden Thema (Alternativantrag) kannst du von der jeweilige Themenseite aus starten. 39.10 +=Thema, Interesse= 39.11 +Auf dieser Seite werden alle Initiativen zu diesem Thema aufgelistet. Wenn du ,,Interesse am Thema'' anmeldest, wirst du für dieses Thema den Mitgliedern des übergeordneten Themenbereichs gleichgestellt und erhöhst die Bemessungsgrundlage der von Initiativen zu erreichenden Unterstützerquoren. Sobald du eine Initiative dieses Themas unterstützt, wird dein Interesse auch automatisch angemeldet. Sofern du allen Initiativen die Unterstützung entziehst, bleibt dein Interesse dennoch bestehen bis du es abmeldest. 39.12 +=Delegation, Auto-Ablehnung= 39.13 +Eine Delegation zu diesem Thema geht einer eventuell vorhandenen globalen Delegation und/oder Delegation für den übergeordneten Themenbereich vor. Eine eventuelle Weisung zur Auto-Ablehung gilt nur für den Fall, dass du nicht selbst an der Endabstimmung teilnimmst und es keine tatsächlich genutzte Delegation gibt. Bei angemeldetem Interesse werden ausgehende Delegationen während der Diskussionsphase ausgesetzt, gelten aber in der Endabstimmung. 39.14 +=Diskussionsphase= 39.15 +Während der Diskussionsphase (Zustände ,,Neu'' und ,,Diskussion'') solltest du **alle** Initiativen, die du grundsätzlich (oder unter bestimmten Bedingungen) für zustimmungsfähig hältst, unterstützen und Anregungen zur Verbesserung geben (Näheres dazu findest du auf der Initiativenseite). Hierdurch gibst du den Initiatoren die Chance, den Entwurf zu verbessern. Während dieser Phase hast du auch die Möglichkeit, eine eigene (konkurrierende) Initiative zu diesem Thema zu starten und um Unterstützung zu ringen. 39.16 +=Endabstimmung= 39.17 +Initiativen, die ein bestimmtes Quorum erreichen, werden in die Endabstimmung übernommen. Du kannst dann alle Initiativen, denen du zustimmen möchtest, in eine Präferenzreihenfolge bringen. Bei den anderen Initiativen kannst Du dich enthalten oder auch dagegen stimmen (letzteres ebenfalls mit Präferenz). Beschlossen werden können nur Initiativen die insgesamt mehr Ja- als Nein-Stimmen erhalten.
40.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 40.2 +++ b/locale/help/vote.list.de.txt Fri Dec 25 12:00:00 2009 +0100 40.3 @@ -0,0 +1,8 @@ 40.4 +=Abstimmmung= 40.5 +Je nachdem, welche Standardeinstellungen du gewählt hast, befinden sich zu Beginn der Abstimmung alle Initiativen im Feld Enthaltung oder im Feld Ablehnung. Beginne am besten mit deinem Favoriten und ziehe ihn mit der Maus in das Feld Zustimmung. Danach wählst du die nächste Initiative, der du zustimmen möchtest und ziehst sie 40.6 +- in die gleiche grüne Box, falls du dieser Initiative gleichberechtigt zustimmen möchtest oder 40.7 +- zwischen die Zustimmungs- und Enthaltungsbox, falls diese Initiative dein Ersatzwunsch sein soll. 40.8 + 40.9 +Auf diese Weise fährst du fort und legst die Präferenzreihenfolge der Initiativen, denen du zustimmen möchtest fest, wobei du gleichwertige Initiativen jeweils in die gleiche Box hineinziehst. Dann legst du deine Enthaltungen und Ablehnungen fest, wobei du auch die Ablehnungen in eine Rangreihenfolge bringen kannst, falls eine der Initiativen so etwas wie das ,,kleinere Übel'' darstellen sollte. 40.10 + 40.11 +**Wichtig:** Verlasse diese Seite über die Schaltfläche ,,Stimmabgabe abschließen''. Bis zum Ende der Abstimmung kannst du deine Angaben jederzeit ändern. Es werden keine Zwischenergebnisse der Abstimmung veröffentlicht. Die Unterstützerangaben auf der Themenübersichtsseite beziehen sich auf die abgeschlossene Diskussion, beziehen sich jeweils auf die einzelne Initiative und sind nur sehr bedingt geeignet, zwischen den Initiativen zu vergleichen. Insbesondere enthalten sie keinerlei Informationen über Präferenzen. Darüber hinaus kann sich der Teilnehmerkreis von Diskussion und Abstimmung erheblich unterscheiden.
41.1 --- a/locale/translations.de.lua Thu Dec 10 12:00:00 2009 +0100 41.2 +++ b/locale/translations.de.lua Fri Dec 25 12:00:00 2009 +0100 41.3 @@ -1,12 +1,15 @@ 41.4 #!/usr/bin/env lua 41.5 return { 41.6 +["#{interested_issues_to_vote_count} issue(s) you are interested in"] = "#{interested_issues_to_vote_count} Themen, die Dich interessieren"; 41.7 +["#{issues_to_vote_count} issue(s)"] = "#{issues_to_vote_count} Themen"; 41.8 ["#{number} Image(s) has been deleted"] = "Es wurde(n) #{number} Bild(er) gelöscht"; 41.9 ["#{number} Image(s) has been updated"] = "Es wurde(n) #{number} Bild(er) aktualisiert"; 41.10 ["(change URL)"] = "(URL ändern)"; 41.11 -["+ #{weight}"] = false; 41.12 -["A-Z"] = false; 41.13 -["About"] = false; 41.14 +["+ #{weight}"] = "+ #{weight}"; 41.15 +["A-Z"] = "A-Z"; 41.16 +["About"] = "About"; 41.17 ["About LiquidFeedback"] = "Über LiquidFeedback"; 41.18 +["Abstention"] = "Enthaltung"; 41.19 ["Accepted at"] = "Angenommen am/um"; 41.20 ["Active?"] = "Aktiv?"; 41.21 ["Add my interest"] = "Mein Interesse anmelden"; 41.22 @@ -17,7 +20,7 @@ 41.23 ["Admin"] = "Admin"; 41.24 ["Admin menu"] = "Admin Menü"; 41.25 ["Admin?"] = "Admin?"; 41.26 -["Administrator"] = false; 41.27 +["Administrator"] = "Administrator"; 41.28 ["Admission time"] = "Zeit für die Zulassung"; 41.29 ["Admitted"] = "zugelassen"; 41.30 ["Any"] = "Alle"; 41.31 @@ -31,7 +34,7 @@ 41.32 ["Author"] = "Autor"; 41.33 ["Autoreject is off."] = "Auto-Ablehnen ist aus"; 41.34 ["Autoreject is on."] = "Auto-Ablehnen ist an"; 41.35 -["Avatar"] = false; 41.36 +["Avatar"] = "Avatar"; 41.37 ["Become a member"] = "Mitglied werden"; 41.38 ["Birthday"] = "Geburtstag"; 41.39 ["Bug report"] = "Fehlerbericht"; 41.40 @@ -49,16 +52,18 @@ 41.41 ["Compare"] = "Vergleichen"; 41.42 ["Contacts"] = "Kontakte"; 41.43 ["Content"] = "Inhalt"; 41.44 +["Counting of votes"] = "Auszählung"; 41.45 ["Create alternative initiative"] = "Alternative Initiative hinzufügen"; 41.46 ["Create new area"] = "Neuen Themenbereich anlegen"; 41.47 ["Create new issue"] = "Neues Thema anlegen"; 41.48 ["Created at"] = "Erzeugt am/um"; 41.49 ["Current draft"] = "Aktueller Entwurf"; 41.50 +["Current votings in areas you are member of and issues you are interested in:"] = "Jetzt laufende Abstimmungen zu Themen aus Deinen Themenbereichen oder solchen an denen Du interessiert bist:"; 41.51 ["Degree"] = "Grad"; 41.52 ["Delegations"] = "Delegationen"; 41.53 ["Description"] = "Beschreibung"; 41.54 ["Details"] = "Details"; 41.55 -["Diff"] = false; 41.56 +["Diff"] = "Differenz"; 41.57 ["Direct member count"] = "Anzahl Direktmitglieder"; 41.58 ["Direct membership"] = "Direkte Mitgliedschaft"; 41.59 ["Discussion"] = "Diskussion"; 41.60 @@ -70,11 +75,12 @@ 41.61 ["Edit initiative"] = "Initiative bearbeiten"; 41.62 ["Edit my page"] = "Meine Seite bearbeiten"; 41.63 ["Edit my profile"] = "Mein Profil bearbeiten"; 41.64 -["Empty help text: #{id}.#{lang}.txt"] = false; 41.65 +["Empty help text: #{id}.#{lang}.txt"] = "Leerer Hilfe-Text: #{id}.#{lang}.txt"; 41.66 ["Error while updating member, database reported:<br /><br /> (#{errormessage})"] = "Fehler beim aktualisieren des Mitglieds, die Datenbank berichtet folgenden Fehler:<br /><br /> (#{errormessage})"; 41.67 ["External memberships"] = "Externe Mitgliedschaften"; 41.68 ["External posts"] = "Externe Ämter"; 41.69 -["Filter"] = false; 41.70 +["Filter"] = "Filter"; 41.71 +["Finish voting"] = "Stimmabgabe abschließen"; 41.72 ["Finished"] = "Abgeschlossen"; 41.73 ["Frozen"] = "Eingefroren"; 41.74 ["Fulfilled"] = "Erfüllt"; 41.75 @@ -101,9 +107,12 @@ 41.76 ["Interest not existant"] = "Interesse existiert nicht"; 41.77 ["Interest removed"] = "Interesse entfernt"; 41.78 ["Interest updated"] = "Interesse aktualisiert"; 41.79 +["Interested"] = "Interessiert"; 41.80 ["Interested members"] = "Interessierte Mitglieder"; 41.81 ["Internal posts"] = "Interne Ämter"; 41.82 ["Invalid username or password!"] = "Ungültiger Benutzername oder Kennwort"; 41.83 +["Invite code"] = "Invite-Code"; 41.84 +["Invite code valid!"] = "Invite-Code gültig!"; 41.85 ["Issue"] = "Thema"; 41.86 ["Issue ##{id}"] = "Issue ##{id}"; 41.87 ["Issue ##{id} (#{policy_name})"] = "Thema ##{id} (#{policy_name})"; 41.88 @@ -112,11 +121,14 @@ 41.89 ["Issue policy"] = "Regelwerk für Thema"; 41.90 ["Issue quorum"] = "Quorum Thema"; 41.91 ["Issues"] = "Themen"; 41.92 +["JavaScript is disabled or not available."] = "JavaScript ist abgeschaltet oder nicht verfügbar."; 41.93 ["Last snapshot:"] = "Letzte Auszählung:"; 41.94 ["Legend:"] = "Legende:"; 41.95 ["License"] = "Lizenz"; 41.96 ["Locked?"] = "Gesperrt?"; 41.97 ["Login"] = "Anmeldung"; 41.98 +["Login is available"] = "Anmeldename ist verfügbar"; 41.99 +["Login name"] = "Anmeldename"; 41.100 ["Login successful!"] = "Anmeldung erfolgreich"; 41.101 ["Logout"] = "Abmelden"; 41.102 ["Logout successful"] = "Abmeldung erfolgreich"; 41.103 @@ -124,16 +136,16 @@ 41.104 ["Mark suggestion as implemented and express satisfaction"] = "Anregung als umgesetzt markieren und Zufriedenheit ausdrücken"; 41.105 ["Mark suggestion as not implemented and express dissatisfaction"] = "Anregung als nicht umgesetzt markieren und Unzufriedenheit ausdrücken"; 41.106 ["Mark suggestion as not implemented and express satisfaction"] = "Anregung als nicht umgesetzt markieren und Zufriedenheit ausdrücken"; 41.107 +["Max potential support"] = "Max. potentielle Unterstützer"; 41.108 +["Max support"] = "Max. Unterstützer"; 41.109 ["Member '#{member}'"] = "Mitglied '#{member}'"; 41.110 ["Member has been removed from your contacts"] = "Mitglied wurde aus Deinen Kontakten entfernt"; 41.111 -["Member has been saved as private contact"] = "Mitglied wurde als privater Kontakt gespeichert"; 41.112 -["Member has been saved as public contact"] = "Mitglied wurde als öffentlicher Kontakt gespeichert"; 41.113 ["Member is administrator"] = "Mitglied ist Administrator"; 41.114 ["Member is already saved in your contacts!"] = "Mitglied ist schon in Deinen Kontakten!"; 41.115 ["Member list"] = "Mitgliederliste"; 41.116 ["Member login"] = "Mitglied Login"; 41.117 ["Member name"] = "Mitglied Name"; 41.118 -["Member page"] = false; 41.119 +["Member page"] = "Mitgliederseite"; 41.120 ["Member successfully registered"] = "Mitglied erfolgreich registriert"; 41.121 ["Member successfully updated"] = "Mitglied erfolgreich aktualisert"; 41.122 ["Member: '#{login}' (#{name})"] = "Mitlied: '#{login}' (#{name})"; 41.123 @@ -143,10 +155,11 @@ 41.124 ["Membership removed"] = "Mitgliedschaft entfernt"; 41.125 ["Membership updated"] = "Mitgliedschaft aktualisiert"; 41.126 ["Memberships"] = "Mitgliedschaften"; 41.127 -["Missing help text: #{id}.#{lang}.txt"] = false; 41.128 +["Missing help text: #{id}.#{lang}.txt"] = "Fehlender Hilfe-Text: #{id}.#{lang}.txt"; 41.129 ["Mobile phone"] = "Mobiltelefon"; 41.130 ["My opinion"] = "Meine Meinung"; 41.131 ["Name"] = "Name"; 41.132 +["Name is available"] = "Name ist verfügbar"; 41.133 ["New"] = "Neu"; 41.134 ["New draft has been added to initiative"] = "Neuer Entwurf wurde der Initiative hinzugefügt"; 41.135 ["New draft revision"] = "Neue Revision des Entwurfs"; 41.136 @@ -155,9 +168,13 @@ 41.137 ["New passwords is too short."] = "Das neue Kennwort ist zu kurz"; 41.138 ["Newest"] = "Neueste"; 41.139 ["Next state"] = "Nächster Zustand"; 41.140 +["No"] = "Nein"; 41.141 ["No changes to your images were made"] = "An Deinen Bildern wurde nichts geändert"; 41.142 ["No delegation"] = "Keine Delegation"; 41.143 ["No membership at all"] = "Gar keine Mitgliedschaft"; 41.144 +["No support at all"] = "Gar keine Unterstützung"; 41.145 +["Not a member"] = "Kein Mitglied"; 41.146 +["Not voted"] = "Nicht abgestimmt"; 41.147 ["Number of incoming delegations, follow link to see more details"] = "Anzahl eingehender Delegationen, Link folgen für mehr Details"; 41.148 ["OK"] = "OK"; 41.149 ["Old draft revision"] = "Alte Revision des Entwurfs"; 41.150 @@ -165,15 +182,26 @@ 41.151 ["Old password"] = "Altes Kennwort"; 41.152 ["Old password is wrong"] = "Das alte Kennwort ist falsch"; 41.153 ["Oldest"] = "Älteste"; 41.154 +["One issue"] = "Ein Thema"; 41.155 +["One issue you are interested in"] = "Ein Thema, das Dich interessiert"; 41.156 ["Order by"] = "Sortieren nach"; 41.157 ["Organizational unit"] = "Organisationseinheit"; 41.158 ["Outgoing delegations"] = "Ausgehende Delegationen"; 41.159 ["Password"] = "Kennwort"; 41.160 +["Password (repeat)"] = "Kennwort (wiederholen)"; 41.161 +["Passwords don't match!"] = "Kennwörter stimmen nicht überein!"; 41.162 +["Passwords must consist of at least 8 characters!"] = "Das Kennwort muß zumindest 8 Zeichen lang sein!"; 41.163 ["Phone"] = "Telefon"; 41.164 ["Photo"] = "Foto"; 41.165 +["Please choose a login name. This name will not be shown to others and is used only by you to login into the system. The login name is case sensitive."] = "Bitte wähle einen Anmeldenamen. Dieser wird anderen nicht gezeigt und nur von Dir zum Anmelden verwendet. Groß- und Kleinschreibung wird berücksichtigt."; 41.166 +["Please choose a name, i.e. your real name or your nick name. This name will be shown to others to identify you. You CAN'T change this name later, so please choose it wisely!"] = "Bitte wähle einen Namen, z. B. Deinen Real- oder Nicknamen. Dieser wird anderen angezeigt um Dich zu identifizieren. Du kannst Deinen Namen später NICHT ändern, wähle ihn also weise!"; 41.167 +["Please choose a password and enter it twice. The password is case sensitive."] = "Bitte wähle ein Kennwort und gebe es zweimal ein. Groß- und Kleinschreibung wird berücksichtigt."; 41.168 +["Please enter the invite code you've received."] = "Bitte gib den Invite-Code ein, den Du erhalten hast."; 41.169 ["Policy"] = "Regelwerk"; 41.170 ["Population"] = "Grundgesamtheit"; 41.171 ["Posts"] = "Ämter"; 41.172 +["Potential support"] = "Potentielle Unterstützung"; 41.173 +["Potential supporter"] = "Potentielle Unterstützer"; 41.174 ["Profession"] = "Beruf"; 41.175 ["Profile"] = "Profil"; 41.176 ["Publish"] = "Veröffentlichen"; 41.177 @@ -182,7 +210,9 @@ 41.178 ["Rank"] = "Rang"; 41.179 ["Real name"] = "Realname"; 41.180 ["Refresh support to current draft"] = "Unterstützung auf aktuellen Entwurf aktualisieren"; 41.181 +["Register"] = "Registrieren"; 41.182 ["Register new member"] = "Neues Mitglied registrieren"; 41.183 +["Registration"] = "Registrierung"; 41.184 ["Remove"] = "Entfernen"; 41.185 ["Remove autoreject"] = "Auto-Ablehnen abschalten"; 41.186 ["Remove from contacts"] = "Aus den Kontakten entfernen"; 41.187 @@ -207,57 +237,72 @@ 41.188 ["Set issue delegation"] = "Delegation für Thema festlegen"; 41.189 ["Show"] = "Zeige"; 41.190 ["Show active members"] = "Zeige aktive Mitglieder"; 41.191 +["Show all initiatives"] = "Zeige alle Initiativen"; 41.192 ["Show areas in use"] = "Zeige verwendete Themenbereiche"; 41.193 ["Show areas not in use"] = "Zeige nicht verwendente Themenbereiche"; 41.194 ["Show diff"] = "Änderungen anzeigen"; 41.195 ["Show locked members"] = "Zeige gesperrte Mitglieder"; 41.196 ["Show member"] = "Mitglied anzeigen"; 41.197 -["Show other initiatives"] = "Zeige alternative Initiativen"; 41.198 -["Software"] = false; 41.199 +["Software"] = "Software"; 41.200 +["Some JavaScript based functions (voting in particular) will not work.\nFor this beta, please use a current version of Firefox, Safari, Opera(?), Konqueror or another (more) standard compliant browser.\nAlternative access without JavaScript will be available soon."] = "Einige auf JavaScript basierende Funktionen (insbesondere der Abstimmung) sind nicht benutzbar.\nFür diese Beta verwende bitte eine aktuelle Version von Firefox, Safari, Opera(?), Konqueror oder einen anderen (mehr) den Standards entsprechenden Browser.\nEin alternativer Zugriff ohne JavaScript wird bald zur Verfügung stehen."; 41.201 +["Sorry, you have reached your personal flood limit. Please be slower..."] = "Sorry, Du hast Dein persönliches Flood-Limit erreicht. Bitte sei langsamer..."; 41.202 +["Sorry, your contingent for creating initiatives has been used up. Please try again later."] = "Sorry, Dein Antragskontingent ist zur Zeit ausgeschöpft. Bitte versuche es später erneut!"; 41.203 ["State"] = "Zustand"; 41.204 -["Statement"] = false; 41.205 +["Statement"] = "Statement"; 41.206 ["Suggestion"] = "Anregung"; 41.207 ["Suggestion currently implemented"] = "Anregung zur Zeit umgesetzt"; 41.208 ["Suggestion currently not implemented"] = "Anregung zur Zeit nicht umgesetzt"; 41.209 ["Suggestion for initiative: '#{name}'"] = "Anregung für Initiative '#{name}'"; 41.210 ["Suggestions"] = "Anregungen"; 41.211 ["Support"] = "Unterstützung"; 41.212 -["Support S+I"] = "Unterstütung S+I"; 41.213 ["Support this initiative"] = "Diese Initiative unterstützen"; 41.214 ["Supported initiatives"] = "Unterstützte Initiativen"; 41.215 ["Supporter"] = "Unterstützer"; 41.216 ["That's me!"] = "Das bin ich"; 41.217 +["The code you've entered is invalid"] = "Den Code den Du eingeben hast ist nicht gültig!"; 41.218 ["The drafts do not differ"] = "Die Entwürfe unterscheiden sich nicht"; 41.219 +["This issue is already closed."] = "Das Thema ist schon geschlossen."; 41.220 +["This issue is already frozen."] = "Das Thema ist schon eingefroren"; 41.221 +["This login is already taken, please choose another one!"] = "Dieser Anmeldename ist bereits vergeben, bitte wähle einen anderen!"; 41.222 +["This name is already taken, please choose another one!"] = "Dieser Name ist bereits vergeben, bitte wähle einen anderen!"; 41.223 ["Time left"] = "Restzeit"; 41.224 -["Title (80 chars max)"] = "Titel (max. 80 Zeichen)"; 41.225 +["Title (80 chars max)"] = "Title (max. 80 Zeichen)"; 41.226 +["Traditional wiki syntax"] = "Traditionaller Wiki-Syntax"; 41.227 ["Trustee"] = "Bevollmächtigter"; 41.228 ["Unknown author"] = "Unbekannter Autor"; 41.229 ["Upload images"] = "Bilder hochladen"; 41.230 -["Username"] = "Benutzername"; 41.231 ["Verification time"] = "Zeit für die Überprüfung"; 41.232 -["Version"] = false; 41.233 +["Version"] = "Version"; 41.234 ["Vote later"] = "Später abstimmen"; 41.235 ["Vote now"] = "Jetzt abstimmen"; 41.236 +["Vote now/later"] = "Jetzt/später abstimmen"; 41.237 +["Voted"] = "Abgestimmt"; 41.238 ["Voting"] = "Abstimmung"; 41.239 +["Voting for this issue has already begun."] = "Die Abstimmung für dieses Thema hat schon begonnen."; 41.240 +["Voting has not started yet."] = "Die Abstimmung hat noch nicht begonnen."; 41.241 ["Voting requests"] = "Abstimmanträge"; 41.242 ["Voting time"] = "Zeit für die Abstimmung"; 41.243 ["Website"] = "Webseite"; 41.244 +["Wiki engine"] = "Wiki engine"; 41.245 +["Yes"] = "Ja"; 41.246 ["You are already not supporting this initiative"] = "Diese Initiative hat bereits keine Unterstützung von Dir"; 41.247 ["You are already supporting the latest draft"] = "Du unterstützt bereits den neuesten Entwurf"; 41.248 -["You are currently not supporting this initiative. By adding suggestions to this initiative you will automatically become a potential supporter."] = false; 41.249 +["You are currently not supporting this initiative. By adding suggestions to this initiative you will automatically become a potential supporter."] = "Du bist zur Zeit kein Unterstützer dieser Initiative. Wenn Du eine Anregung hinzufügst wirst Du automatisch potentieller Unterstützer!"; 41.250 ["You are member"] = "Du bist Mitglied"; 41.251 ["You didn't saved any member as contact yet."] = "Du hast noch kein Mitglied als Kontakt gespeichert!"; 41.252 ["You have saved this member as contact"] = "Du hast das Mitglied als Kontakt gespeichert"; 41.253 ["You have saved this member as contact."] = "Du hast das Mitglied als Kontakt gespeichert."; 41.254 ["You need to be logged in, to use this system."] = "Du musst eingeloggt sein, um das System zu benutzen"; 41.255 +["You've successfully registered and you can login now with your login and password!"] = "Du hast Dich erfolgreich registriert und kannst Dich jetzt mit Deinen Benutzernamen und Kennwort anmelden!"; 41.256 ["Your are interested"] = "Du bist interessiert"; 41.257 +["Your are potential supporter"] = "Du bist potentieller Unterstützer"; 41.258 ["Your are supporter"] = "Du bist Unterstützer"; 41.259 ["Your delegation for this area has been deleted."] = "Deine Delegation für dieses Themengebiet wurde gelöscht"; 41.260 ["Your delegation for this area has been updated."] = "Deine Delegation für dieses Themengebiet wurde geändert"; 41.261 ["Your delegation for this issue has been deleted."] = "Deine Delegation für dieses Thema wurde gelöscht"; 41.262 ["Your delegation for this issue has been updated."] = "Deine Delegation für dieses Thema wurde geändert"; 41.263 ["Your global delegation has been deleted."] = "Deine globale Delegation wurde gelöscht"; 41.264 -["Your global delegation has been updated."] = "Deine globale Delegation würde geändert"; 41.265 +["Your global delegation has been updated."] = "Deine globale Delegation wurde geändert"; 41.266 ["Your opinion has been deleted"] = "Deine Meinung wurde gelöscht"; 41.267 ["Your opinion has been updated"] = "Deine Meinung wurde aktualisiert"; 41.268 ["Your page has been updated"] = "Deine Seite wurde aktualisiert"; 41.269 @@ -266,11 +311,12 @@ 41.270 ["Your support has been added to this initiative"] = "Deine Unterstützung wurde der Initiative hinzugefügt"; 41.271 ["Your support has been removed from this initiative"] = "Deine Unterstützung wurde der Initiave entzogen"; 41.272 ["Your support has been updated to the latest draft"] = "Deine Unterstützung wurde auf den neuesten Entwurf aktualisiert"; 41.273 -["Z-A"] = false; 41.274 +["Your web browser is not fully supported yet."] = "Dein Web-Browser wird noch nicht vollständig unterstützt."; 41.275 +["Z-A"] = "Z-A"; 41.276 ["all"] = "Alle"; 41.277 -["blank"] = false; 41.278 ["delete<br /><br />"] = "löschen<br /><br />"; 41.279 -["email"] = false; 41.280 +["email"] = "E-Mail"; 41.281 +["login name"] = "Anmeldename"; 41.282 ["must"] = "muss"; 41.283 ["must not"] = "darf nicht"; 41.284 ["must/should"] = "muss/soll"; 41.285 @@ -279,5 +325,5 @@ 41.286 ["not implemented"] = "nicht umgesetzt"; 41.287 ["should"] = "soll"; 41.288 ["should not"] = "soll nicht"; 41.289 -["xmpp"] = false; 41.290 +["xmpp"] = "Jabber (XMPP)"; 41.291 }
42.1 --- a/model/direct_voter.lua Thu Dec 10 12:00:00 2009 +0100 42.2 +++ b/model/direct_voter.lua Fri Dec 25 12:00:00 2009 +0100 42.3 @@ -1,5 +1,6 @@ 42.4 DirectVoter = mondelefant.new_class() 42.5 -DirectVoter.table = 'member' 42.6 +DirectVoter.table = 'direct_voter' 42.7 +DirectVoter.primary_key = { "issue_id", "member_id" } 42.8 42.9 DirectVoter:add_reference{ 42.10 mode = 'm1', 42.11 @@ -16,3 +17,10 @@ 42.12 that_key = 'id', 42.13 ref = 'member', 42.14 } 42.15 + 42.16 +function DirectVoter:by_pk(issue_id, member_id) 42.17 + return self:new_selector() 42.18 + :add_where{ "issue_id = ? AND member_id = ?", issue_id, member_id } 42.19 + :optional_object_mode() 42.20 + :exec() 42.21 +end 42.22 \ No newline at end of file
43.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 43.2 +++ b/model/invite_code.lua Fri Dec 25 12:00:00 2009 +0100 43.3 @@ -0,0 +1,10 @@ 43.4 +InviteCode = mondelefant.new_class() 43.5 +InviteCode.table = 'invite_code' 43.6 +InviteCode.primary_key = "code" 43.7 + 43.8 +function InviteCode:by_code(code) 43.9 + local selector = self:new_selector() 43.10 + selector:add_where{'"code" = ?', code } 43.11 + selector:optional_object_mode() 43.12 + return selector:exec() 43.13 +end
44.1 --- a/model/issue.lua Thu Dec 10 12:00:00 2009 +0100 44.2 +++ b/model/issue.lua Fri Dec 25 12:00:00 2009 +0100 44.3 @@ -119,11 +119,7 @@ 44.4 function Issue.object_get:state() 44.5 if self.accepted then 44.6 if self.closed then 44.7 - if self.ranks_available then 44.8 - return "finished" 44.9 - else 44.10 - return "cancelled" 44.11 - end 44.12 + return "finished" 44.13 elseif self.fully_frozen then 44.14 return "voting" 44.15 elseif self.half_frozen then
45.1 --- a/model/member.lua Thu Dec 10 12:00:00 2009 +0100 45.2 +++ b/model/member.lua Fri Dec 25 12:00:00 2009 +0100 45.3 @@ -245,7 +245,7 @@ 45.4 45.5 function Member:by_login_and_password(login, password) 45.6 local selector = self:new_selector() 45.7 - selector:add_where{'"login" = ?', login, password } 45.8 + selector:add_where{'"login" = ?', login } 45.9 selector:add_where('"active"') 45.10 selector:optional_object_mode() 45.11 local member = selector:exec() 45.12 @@ -256,6 +256,20 @@ 45.13 end 45.14 end 45.15 45.16 +function Member:by_login(login) 45.17 + local selector = self:new_selector() 45.18 + selector:add_where{'"login" = ?', login } 45.19 + selector:optional_object_mode() 45.20 + return selector:exec() 45.21 +end 45.22 + 45.23 +function Member:by_name(name) 45.24 + local selector = self:new_selector() 45.25 + selector:add_where{'"name" = ?', name } 45.26 + selector:optional_object_mode() 45.27 + return selector:exec() 45.28 +end 45.29 + 45.30 function Member:get_search_selector(search_string) 45.31 return self:new_selector() 45.32 :add_field( {'"highlight"("member"."name", ?)', search_string }, "name_highlighted")
46.1 --- a/model/policy.lua Thu Dec 10 12:00:00 2009 +0100 46.2 +++ b/model/policy.lua Fri Dec 25 12:00:00 2009 +0100 46.3 @@ -7,6 +7,5 @@ 46.4 this_key = 'id', 46.5 that_key = 'policy_id', 46.6 ref = 'issues', 46.7 - back_ref = 'policy', 46.8 - default_order = '"created", "id"' 46.9 + back_ref = 'policy' 46.10 }
47.1 --- a/model/supporter.lua Thu Dec 10 12:00:00 2009 +0100 47.2 +++ b/model/supporter.lua Fri Dec 25 12:00:00 2009 +0100 47.3 @@ -25,3 +25,12 @@ 47.4 :optional_object_mode() 47.5 :exec() 47.6 end 47.7 + 47.8 +function Supporter.object:has_critical_opinion() 47.9 + return Opinion:new_selector() 47.10 + :add_where{ "initiative_id = ?", self.initiative_id } 47.11 + :add_where{ "member_id = ?", self.member_id } 47.12 + :add_where("(degree = -2 AND fulfilled) OR (degree = 2 AND NOT fulfilled)") 47.13 + :limit(1) 47.14 + :exec()[1] and true or false 47.15 +end 47.16 \ No newline at end of file
48.1 --- a/model/vote.lua Thu Dec 10 12:00:00 2009 +0100 48.2 +++ b/model/vote.lua Fri Dec 25 12:00:00 2009 +0100 48.3 @@ -1,5 +1,6 @@ 48.4 Vote = mondelefant.new_class() 48.5 Vote.table = 'vote' 48.6 +Vote.primary_key = { "initiative_id", "member_id" } 48.7 48.8 Vote:add_reference{ 48.9 mode = 'm1', 48.10 @@ -24,3 +25,10 @@ 48.11 that_key = 'id', 48.12 ref = 'author', 48.13 } 48.14 + 48.15 +function Vote:by_pk(initiative_id, member_id) 48.16 + return self:new_selector() 48.17 + :add_where{ "initiative_id = ? AND member_id = ?", initiative_id, member_id } 48.18 + :optional_object_mode() 48.19 + :exec() 48.20 +end 48.21 \ No newline at end of file
49.1 Binary file static/icons/16/email_open.png has changed
50.1 Binary file static/icons/16/eye.png has changed
51.1 Binary file static/icons/16/thumb_up.png has changed
52.1 Binary file static/icons/16/time.png has changed
53.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 53.2 +++ b/static/js/browser_warning.js Fri Dec 25 12:00:00 2009 +0100 53.3 @@ -0,0 +1,9 @@ 53.4 +function evilBrowser() { 53.5 + return (navigator.appName == "Microsoft Internet Explorer" || ! window.addEventListener); 53.6 +} 53.7 + 53.8 +function checkBrowser(message) { 53.9 + if (evilBrowser()) { 53.10 + alert(message); 53.11 + } 53.12 +}
54.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 54.2 +++ b/static/js/dragdrop.js Fri Dec 25 12:00:00 2009 +0100 54.3 @@ -0,0 +1,73 @@ 54.4 +window.addEventListener("load", function(event) { 54.5 + var originalElement; 54.6 + var draggedElement; 54.7 + var mouseX; 54.8 + var mouseY; 54.9 + var mouseOffsetX; 54.10 + var mouseOffsetY; 54.11 + var elementOffsetX; 54.12 + var elementOffsetY; 54.13 + var dropFunc; 54.14 + var dragElement = function(element, func) { 54.15 + //if (typeof(element) == "string") element = document.getElementById(element); 54.16 + originalElement = element; 54.17 + draggedElement = originalElement.cloneNode(true); 54.18 + originalElement.style.visibility = "hidden"; 54.19 + draggedElement.style.margin = 0; 54.20 + draggedElement.style.position = "absolute"; 54.21 + draggedElement.style.left = elementOffsetX = originalElement.offsetLeft; 54.22 + draggedElement.style.top = elementOffsetY = originalElement.offsetTop; 54.23 + draggedElement.style.width = originalElement.clientWidth; 54.24 + draggedElement.style.height = originalElement.clientHeight; 54.25 + draggedElement.style.backgroundColor = "#eee"; 54.26 + draggedElement.style.opacity = 0.8; 54.27 + originalElement.offsetParent.appendChild(draggedElement); 54.28 + // workaround for wrong clientWidth and clientHeight information: 54.29 + draggedElement.style.width = 2*originalElement.clientWidth - draggedElement.clientWidth; 54.30 + draggedElement.style.height = 2*originalElement.clientHeight - draggedElement.clientHeight; 54.31 + mouseOffsetX = mouseX; 54.32 + mouseOffsetY = mouseY; 54.33 + dropFunc = func; 54.34 + }; 54.35 + window.addEventListener("mousemove", function(event) { 54.36 + mouseX = event.pageX; 54.37 + mouseY = event.pageY; 54.38 + if (draggedElement) { 54.39 + draggedElement.style.left = elementOffsetX + mouseX - mouseOffsetX; 54.40 + draggedElement.style.top = elementOffsetY + mouseY - mouseOffsetY; 54.41 + } 54.42 + }, true); 54.43 + var mouseDrop = function(event) { 54.44 + if (draggedElement) { 54.45 + dropFunc( 54.46 + originalElement, 54.47 + elementOffsetX + mouseX - mouseOffsetX, 54.48 + elementOffsetY + mouseY - mouseOffsetY 54.49 + ); 54.50 + originalElement.style.visibility = ''; 54.51 + draggedElement.parentNode.removeChild(draggedElement); 54.52 + originalElement = null; 54.53 + draggedElement = null; 54.54 + } 54.55 + }; 54.56 + window.addEventListener("mouseup", mouseDrop, true); 54.57 + window.addEventListener("mousedown", mouseDrop, true); 54.58 + var elements = document.getElementsByTagName("*"); 54.59 + for (var i=0; i<elements.length; i++) { 54.60 + var element = elements[i]; 54.61 + if (element.className == "movable") { 54.62 + element.addEventListener("mousedown", function(event) { 54.63 + event.target.style.cursor = "move"; 54.64 + dragElement(event.currentTarget, function(element, dropX, dropY) { 54.65 + event.target.style.cursor = null; 54.66 + elementDropped(element, dropX, dropY); 54.67 + }); 54.68 + event.preventDefault(); 54.69 + }, false); 54.70 + } else if (element.className == "clickable") { 54.71 + element.addEventListener("mousedown", function(event) { 54.72 + event.stopPropagation(); 54.73 + }, false); 54.74 + } 54.75 + } 54.76 +}, false);
55.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 55.2 +++ b/static/js/voting.js Fri Dec 25 12:00:00 2009 +0100 55.3 @@ -0,0 +1,227 @@ 55.4 +function setCategoryHeadings() { 55.5 + var approvalCount = 0; 55.6 + var disapprovalCount = 0; 55.7 + var sections = document.getElementById("voting").childNodes; 55.8 + for (var i=0; i<sections.length; i++) { 55.9 + var section = sections[i]; 55.10 + if (section.className == "approval") approvalCount++; 55.11 + if (section.className == "disapproval") disapprovalCount++; 55.12 + } 55.13 + var approvalIndex = 0; 55.14 + var disapprovalIndex = 0; 55.15 + for (var i=0; i<sections.length; i++) { 55.16 + var section = sections[i]; 55.17 + if ( 55.18 + section.className == "approval" || 55.19 + section.className == "abstention" || 55.20 + section.className == "disapproval" 55.21 + ) { 55.22 + var setHeading = function(heading) { 55.23 + var headingNodes = section.childNodes; 55.24 + for (var j=0; j<headingNodes.length; j++) { 55.25 + var headingNode = headingNodes[j]; 55.26 + if (headingNode.className == "cathead") { 55.27 + headingNode.textContent = heading; 55.28 + } 55.29 + } 55.30 + } 55.31 + var count = 0; 55.32 + var entries = section.childNodes; 55.33 + for (var j=0; j<entries.length; j++) { 55.34 + var entry = entries[j]; 55.35 + if (entry.className == "movable") count++; 55.36 + } 55.37 + if (section.className == "approval") { 55.38 + if (approvalCount > 1) { 55.39 + if (approvalIndex == 0) { 55.40 + if (count == 1) setHeading("Zustimmung (Erstwunsch)"); 55.41 + else setHeading("Zustimmung (Erstwünsche)"); 55.42 + } else if (approvalIndex == 1) { 55.43 + if (count == 1) setHeading("Zustimmung (Zweitwunsch)"); 55.44 + else setHeading("Zustimmung (Zweitwünsche)"); 55.45 + } else if (approvalIndex == 2) { 55.46 + if (count == 1) setHeading("Zustimmung (Drittwunsch)"); 55.47 + else setHeading("Zustimmung (Drittwünsche)"); 55.48 + } else { 55.49 + if (count == 1) setHeading("Zustimmung (" + (approvalIndex+1) + ".-Wunsch)"); 55.50 + else setHeading("Zustimmung (" + (approvalIndex+1) + ".-Wünsche)"); 55.51 + } 55.52 + } else { 55.53 + setHeading("Zustimmung"); 55.54 + } 55.55 + approvalIndex++; 55.56 + } else if (section.className == "abstention") { 55.57 + setHeading("Enthaltung"); 55.58 + } else if (section.className == "disapproval") { 55.59 + if (disapprovalCount > disapprovalIndex + 2) { 55.60 + setHeading("Ablehnung (jedoch Bevorzugung gegenüber unteren Ablehnungsblöcken)") 55.61 + } else if (disapprovalCount == 2 && disapprovalIndex == 0) { 55.62 + setHeading("Ablehnung (jedoch Bevorzugung gegenüber unterem Ablehnungsblock)") 55.63 + } else if (disapprovalIndex == disapprovalCount - 2) { 55.64 + setHeading("Ablehnung (jedoch Bevorzugung gegenüber letztem Ablehnungsblock)") 55.65 + } else { 55.66 + setHeading("Ablehnung"); 55.67 + } 55.68 + disapprovalIndex++; 55.69 + } 55.70 + } 55.71 + } 55.72 +} 55.73 +function elementDropped(element, dropX, dropY) { 55.74 + var oldParent = element.parentNode; 55.75 + var centerY = dropY + element.clientHeight / 2 55.76 + var approvalCount = 0; 55.77 + var disapprovalCount = 0; 55.78 + var mainDiv = document.getElementById("voting"); 55.79 + var sections = mainDiv.childNodes; 55.80 + for (var i=0; i<sections.length; i++) { 55.81 + var section = sections[i]; 55.82 + if (section.className == "approval") approvalCount++; 55.83 + if (section.className == "disapproval") disapprovalCount++; 55.84 + } 55.85 + for (var i=0; i<sections.length; i++) { 55.86 + var section = sections[i]; 55.87 + if ( 55.88 + section.className == "approval" || 55.89 + section.className == "abstention" || 55.90 + section.className == "disapproval" 55.91 + ) { 55.92 + if ( 55.93 + centerY >= section.offsetTop && 55.94 + centerY < section.offsetTop + section.clientHeight 55.95 + ) { 55.96 + var entries = section.childNodes; 55.97 + for (var j=0; j<entries.length; j++) { 55.98 + var entry = entries[j]; 55.99 + if (entry.className == "movable") { 55.100 + if (centerY < entry.offsetTop + entry.clientHeight / 2) { 55.101 + if (element != entry) { 55.102 + oldParent.removeChild(element); 55.103 + section.insertBefore(element, entry); 55.104 + } 55.105 + break; 55.106 + } 55.107 + } 55.108 + } 55.109 + if (j == entries.length) { 55.110 + oldParent.removeChild(element); 55.111 + section.appendChild(element); 55.112 + } 55.113 + break; 55.114 + } 55.115 + } 55.116 + } 55.117 + if (i == sections.length) { 55.118 + var newSection = document.createElement("div"); 55.119 + var cathead = document.createElement("div"); 55.120 + cathead.setAttribute("class", "cathead"); 55.121 + newSection.appendChild(cathead); 55.122 + for (var i=0; i<sections.length; i++) { 55.123 + var section = sections[i]; 55.124 + if ( 55.125 + section.className == "approval" || 55.126 + section.className == "abstention" || 55.127 + section.className == "disapproval" 55.128 + ) { 55.129 + if (centerY < section.offsetTop + section.clientHeight / 2) { 55.130 + if (section.className == "disapproval") { 55.131 + newSection.setAttribute("class", "disapproval"); 55.132 + disapprovalCount++; 55.133 + } else { 55.134 + newSection.setAttribute("class", "approval"); 55.135 + approvalCount++; 55.136 + } 55.137 + mainDiv.insertBefore(newSection, section); 55.138 + break; 55.139 + } 55.140 + } 55.141 + } 55.142 + if (i == sections.length) { 55.143 + newSection.setAttribute("class", "disapproval"); 55.144 + disapprovalCount++; 55.145 + mainDiv.appendChild(newSection); 55.146 + } 55.147 + oldParent.removeChild(element); 55.148 + newSection.appendChild(element); 55.149 + } 55.150 + sections = mainDiv.childNodes; 55.151 + for (i=0; i<sections.length; i++) { 55.152 + var section = sections[i]; 55.153 + if ( 55.154 + (section.className == "approval" && approvalCount > 1) || 55.155 + (section.className == "disapproval" && disapprovalCount > 1) 55.156 + ) { 55.157 + var entries = section.childNodes; 55.158 + for (var j=0; j<entries.length; j++) { 55.159 + var entry = entries[j]; 55.160 + if (entry.className == "movable") break; 55.161 + } 55.162 + if (j == entries.length) { 55.163 + section.parentNode.removeChild(section); 55.164 + } 55.165 + } 55.166 + } 55.167 + setCategoryHeadings(); 55.168 +} 55.169 +window.addEventListener("load", function(event) { 55.170 + setCategoryHeadings(); 55.171 + var mainDiv = document.getElementById("voting"); 55.172 + var form = document.getElementById("voting_form"); 55.173 + var elements = document.getElementsByTagName("input"); 55.174 + for (var i=0; i<elements.length; i++) { 55.175 + var element = elements[i]; 55.176 + if (element.className == "voting_done") { 55.177 + element.addEventListener("click", function(event) { 55.178 + var scoringString = ""; 55.179 + var approvalCount = 0; 55.180 + var disapprovalCount = 0; 55.181 + var sections = mainDiv.childNodes; 55.182 + for (var j=0; j<sections.length; j++) { 55.183 + var section = sections[j]; 55.184 + if (section.className == "approval") approvalCount++; 55.185 + if (section.className == "disapproval") disapprovalCount++; 55.186 + } 55.187 + var approvalIndex = 0; 55.188 + var disapprovalIndex = 0; 55.189 + for (var j=0; j<sections.length; j++) { 55.190 + var section = sections[j]; 55.191 + if ( 55.192 + section.className == "approval" || 55.193 + section.className == "abstention" || 55.194 + section.className == "disapproval" 55.195 + ) { 55.196 + var score; 55.197 + if (section.className == "approval") { 55.198 + score = approvalCount - approvalIndex; 55.199 + approvalIndex++; 55.200 + } else if (section.className == "abstention") { 55.201 + score = 0; 55.202 + } else if (section.className == "disapproval") { 55.203 + score = -1 - disapprovalIndex; 55.204 + disapprovalIndex++; 55.205 + } 55.206 + var entries = section.childNodes; 55.207 + for (var k=0; k<entries.length; k++) { 55.208 + var entry = entries[k]; 55.209 + if (entry.className == "movable") { 55.210 + var id = entry.id.match(/[0-9]+/); 55.211 + var field = document.createElement("input"); 55.212 + scoringString += id + ":" + score + ";"; 55.213 + } 55.214 + } 55.215 + } 55.216 + } 55.217 + var fields = form.childNodes; 55.218 + for (var j=0; j<fields.length; j++) { 55.219 + var field = fields[j]; 55.220 + if (field.name == "scoring") { 55.221 + field.setAttribute("value", scoringString); 55.222 + form.submit(); 55.223 + return; 55.224 + } 55.225 + } 55.226 + alert('Hidden input field named "scoring" not found.'); 55.227 + }, false); 55.228 + } 55.229 + } 55.230 +}, false);
56.1 --- a/static/style.css Thu Dec 10 12:00:00 2009 +0100 56.2 +++ b/static/style.css Fri Dec 25 12:00:00 2009 +0100 56.3 @@ -94,6 +94,7 @@ 56.4 } 56.5 56.6 .topbar a { 56.7 + background-color: #444; 56.8 color: #fff; 56.9 } 56.10 56.11 @@ -200,7 +201,7 @@ 56.12 } 56.13 56.14 .title a { 56.15 - color: #fff; 56.16 + color: #000; 56.17 } 56.18 56.19 .member_image_avatar { 56.20 @@ -220,7 +221,7 @@ 56.21 .actions a { 56.22 float: left; 56.23 display: block; 56.24 - padding: 0.5ex 0.5em 0.5ex 0.0em; 56.25 + padding: 1px 0.5em 1px 0.0em; 56.26 margin-right: 1em; 56.27 vertical-align: middle; 56.28 } 56.29 @@ -262,19 +263,20 @@ 56.30 margin-right: 1em; 56.31 } 56.32 56.33 -.interest .head_active { 56.34 - background-color: #dfd; 56.35 - border: 1px solid #8b8; 56.36 +.interest .head_active, 56.37 +.slot_support .head_potential_supporter { 56.38 + background-color: #fec; 56.39 + border: 1px solid #b96; 56.40 } 56.41 56.42 -.slot_support .head_active { 56.43 - background-color: #dfd; 56.44 +.slot_support .head_supporter { 56.45 + background-color: #dfc; 56.46 border: 1px solid #8b8; 56.47 } 56.48 56.49 .delegation .head_active { 56.50 - background-color: #ffd; 56.51 - border: 1px solid #bb8; 56.52 + background-color: #ddf; 56.53 + border: 1px solid #88b; 56.54 } 56.55 56.56 .vote_info .close { 56.57 @@ -389,44 +391,26 @@ 56.58 } 56.59 56.60 /************************************************************************* 56.61 + * ui.filter 56.62 * ui.order 56.63 */ 56.64 56.65 -.ui_filter_head { 56.66 +.ui_filter_head, 56.67 +.ui_order_head { 56.68 color: #777; 56.69 - float: left; 56.70 - margin-bottom: 1ex; 56.71 + margin-top: 1ex; 56.72 + margin-bottom: 1.5ex; 56.73 font-size: 75%; 56.74 } 56.75 56.76 -.ui_filter_head a { 56.77 - color: #777; 56.78 - padding: 0.5ex; 56.79 -} 56.80 - 56.81 -.ui_filter_head a.active{ 56.82 - color: #fff; 56.83 - background-color: #777; 56.84 - padding: 0.5ex; 56.85 -} 56.86 - 56.87 -/************************************************************************* 56.88 - * ui.order 56.89 - */ 56.90 - 56.91 -.ui_order_head { 56.92 - color: #777; 56.93 - text-align: right; 56.94 - margin-bottom: 1ex; 56.95 - font-size: 75%; 56.96 -} 56.97 - 56.98 +.ui_filter_head a, 56.99 .ui_order_head a { 56.100 color: #777; 56.101 padding: 0.5ex; 56.102 } 56.103 56.104 -.ui_order_head a.active{ 56.105 +.ui_filter_head a.active, 56.106 +.ui_order_head a.active { 56.107 color: #fff; 56.108 background-color: #777; 56.109 padding: 0.5ex; 56.110 @@ -643,7 +627,11 @@ 56.111 padding: 0.3ex 0.5em 0.3ex 0.5em; 56.112 } 56.113 56.114 - 56.115 +.suggestion_my_opinion a { 56.116 + white-space: nowrap; 56.117 + padding-left: 0.2ex !important; 56.118 + padding-right: 0.2ex !important; 56.119 +} 56.120 56.121 .active { 56.122 background-color: #444; 56.123 @@ -880,3 +868,53 @@ 56.124 margin-bottom: 2ex; 56.125 padding: 1ex; 56.126 } 56.127 + 56.128 + 56.129 +/************************************************************************* 56.130 + * Voting 56.131 + */ 56.132 + 56.133 +#voting { 56.134 + position: relative; 56.135 +} 56.136 +#voting .approval, .abstention, .disapproval { 56.137 + border: 2px black solid; 56.138 + margin-top: 5ex; 56.139 + margin-bottom: 5ex; 56.140 + padding: 1ex; 56.141 + padding-bottom: 2ex; 56.142 +} 56.143 +#voting .approval { 56.144 + background-color: #9f9; 56.145 +} 56.146 +#voting .approval .movable { 56.147 + background-color: #dfd; 56.148 +} 56.149 +#voting .abstention { 56.150 + background-color: #ccc; 56.151 +} 56.152 +#voting .abstention .movable { 56.153 + background-color: #eee; 56.154 +} 56.155 +#voting .disapproval { 56.156 + background-color: #f88; 56.157 +} 56.158 +#voting .disapproval .movable { 56.159 + background-color: #fbb; 56.160 +} 56.161 +#voting .cathead { 56.162 + font-weight: bold; 56.163 +} 56.164 +#voting .movable { 56.165 + position: relative; 56.166 + border: 1px black solid; 56.167 + margin: 1ex; 56.168 + padding: 0.5ex; 56.169 + cursor: pointer; 56.170 +} 56.171 +#voting .clickable { 56.172 + cursor: auto; 56.173 +} 56.174 +#voting a.clickable { 56.175 + cursor: pointer; 56.176 +}