liquid_feedback_frontend

annotate app/main/vote/list.lua @ 159:5d797c6706d5

implement quorum display

show the initiative quorum as a small 1px line in bargraph
allow to update your support on the diff page
better linked title in diff page
show absolute quorum numbers in detail pages of issue and initiative
author Daniel Poelzleithner <poelzi@poelzi.org>
date Sat Oct 09 03:42:48 2010 +0200 (2010-10-09)
parents 39d49cf933d8
children 068ad175ebd7
rev   line source
bsw/jbe@19 1 local issue = Issue:by_id(param.get("issue_id"), atom.integer)
bsw/jbe@19 2
bsw/jbe@19 3 local member_id = param.get("member_id", atom.integer)
bsw/jbe@19 4 local member
bsw/jbe@19 5
bsw/jbe@19 6 local readonly = false
poelzi@156 7
bsw/jbe@19 8 if member_id then
bsw/jbe@19 9 if not issue.closed then
bsw/jbe@19 10 error("access denied")
bsw/jbe@19 11 end
bsw/jbe@19 12 member = Member:by_id(member_id)
bsw/jbe@19 13 readonly = true
bsw/jbe@19 14 end
bsw/jbe@19 15
poelzi@138 16 if issue.closed then
poelzi@156 17 if not member then
poelzi@156 18 slot.put_into("error", _"This issue is already closed.")
poelzi@156 19 end
poelzi@158 20 if not member then
poelzi@158 21 member = app.session.member
poelzi@158 22 end
poelzi@156 23 readonly = true
poelzi@138 24 end
poelzi@138 25
bsw/jbe@19 26 if member then
poelzi@156 27 local str = _("Ballot of '#{member_name}' for issue ##{issue_id}",
poelzi@156 28 {member_name = string.format('<a href="%s">%s</a>',
poelzi@156 29 encode.url{
poelzi@156 30 module = "member",
poelzi@156 31 view = "show",
poelzi@156 32 id = member.id,
poelzi@156 33 },
poelzi@156 34 encode.html(member.name)),
poelzi@156 35 issue_id = string.format('<a href="%s">%s</a>',
poelzi@156 36 encode.url{
poelzi@156 37 module = "issue",
poelzi@156 38 view = "show",
poelzi@156 39 id = issue.id,
poelzi@156 40 },
poelzi@156 41 encode.html(tostring(issue.id)))
poelzi@156 42 }
poelzi@156 43 )
poelzi@156 44 slot.put_into("title", str)
bsw/jbe@19 45 else
bsw/jbe@19 46 member = app.session.member
bsw/jbe@19 47 slot.put_into("title", _"Voting")
bsw/jbe@19 48
bsw/jbe@19 49 slot.select("actions", function()
bsw/jbe@19 50 ui.link{
bsw/jbe@19 51 content = function()
bsw/jbe@19 52 ui.image{ static = "icons/16/cancel.png" }
bsw/jbe@19 53 slot.put(_"Cancel")
bsw/jbe@19 54 end,
bsw/jbe@19 55 module = "issue",
bsw/jbe@19 56 view = "show",
bsw/jbe@19 57 id = issue.id
bsw/jbe@19 58 }
bsw@26 59 ui.link{
bsw@86 60 text = _"Discard voting",
bsw@26 61 content = function()
bsw@26 62 ui.image{ static = "icons/16/email_delete.png" }
bsw@26 63 slot.put(_"Discard voting")
bsw@26 64 end,
bsw@26 65 module = "vote",
bsw@26 66 action = "update",
bsw@26 67 params = {
bsw@26 68 issue_id = issue.id,
bsw@26 69 discard = true
bsw@26 70 },
bsw@26 71 routing = {
bsw@26 72 default = {
bsw@26 73 mode = "redirect",
bsw@26 74 module = "issue",
bsw@26 75 view = "show",
bsw@26 76 id = issue.id
bsw@26 77 }
bsw@26 78 }
bsw@26 79 }
bsw/jbe@19 80 end)
bsw/jbe@19 81 end
bsw/jbe@19 82
bsw/jbe@19 83
bsw/jbe@5 84 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."
bsw/jbe@5 85
bsw/jbe@5 86 ui.script{ static = "js/browser_warning.js" }
bsw/jbe@5 87 ui.script{ script = "checkBrowser(" .. encode.json(_"Your web browser is not fully supported yet." .. " " .. warning_text:gsub("\n", "\n\n")) .. ");" }
bsw/jbe@5 88
bsw/jbe@19 89
bsw/jbe@19 90 local tempvoting_string = param.get("scoring")
bsw/jbe@19 91
bsw/jbe@19 92 local tempvotings = {}
bsw/jbe@19 93 if tempvoting_string then
bsw/jbe@19 94 for match in tempvoting_string:gmatch("([^;]+)") do
bsw/jbe@19 95 for initiative_id, grade in match:gmatch("([^:;]+):([^:;]+)") do
bsw/jbe@19 96 tempvotings[tonumber(initiative_id)] = tonumber(grade)
bsw/jbe@19 97 end
bsw/jbe@5 98 end
bsw/jbe@19 99 end
bsw/jbe@5 100
bsw@95 101 local initiatives = issue:get_reference_selector("initiatives"):add_where("initiative.admitted"):add_order_by("initiative.satisfied_supporter_count DESC"):exec()
bsw/jbe@5 102
bsw/jbe@5 103 local min_grade = -1;
bsw/jbe@5 104 local max_grade = 1;
bsw/jbe@5 105
bsw/jbe@5 106 for i, initiative in ipairs(initiatives) do
bsw/jbe@5 107 -- TODO performance
bsw/jbe@19 108 initiative.vote = Vote:by_pk(initiative.id, member.id)
bsw/jbe@19 109 if tempvotings[initiative.id] then
bsw/jbe@19 110 initiative.vote = {}
bsw/jbe@19 111 initiative.vote.grade = tempvotings[initiative.id]
bsw/jbe@19 112 end
bsw/jbe@5 113 if initiative.vote then
bsw/jbe@5 114 if initiative.vote.grade > max_grade then
bsw/jbe@5 115 max_grade = initiative.vote.grade
bsw/jbe@5 116 end
bsw/jbe@5 117 if initiative.vote.grade < min_grade then
bsw/jbe@5 118 min_grade = initiative.vote.grade
bsw/jbe@5 119 end
bsw/jbe@5 120 end
bsw/jbe@5 121 end
bsw/jbe@5 122
bsw/jbe@5 123 local sections = {}
bsw/jbe@5 124 for i = min_grade, max_grade do
bsw/jbe@5 125 sections[i] = {}
bsw/jbe@5 126 for j, initiative in ipairs(initiatives) do
bsw/jbe@5 127 if (initiative.vote and initiative.vote.grade == i) or (not initiative.vote and i == 0) then
bsw/jbe@5 128 sections[i][#(sections[i])+1] = initiative
bsw/jbe@5 129 end
bsw/jbe@5 130 end
bsw/jbe@5 131 end
bsw/jbe@5 132
bsw/jbe@19 133 local approval_count, disapproval_count = 0, 0
bsw/jbe@19 134 for i = min_grade, -1 do
bsw/jbe@19 135 if #sections[i] > 0 then
bsw/jbe@19 136 disapproval_count = disapproval_count + 1
bsw/jbe@19 137 end
bsw/jbe@19 138 end
bsw/jbe@19 139 local approval_count = 0
bsw/jbe@19 140 for i = 1, max_grade do
bsw/jbe@19 141 if #sections[i] > 0 then
bsw/jbe@19 142 approval_count = approval_count + 1
bsw/jbe@19 143 end
bsw/jbe@19 144 end
bsw/jbe@5 145
bsw/jbe@5 146
bsw/jbe@5 147
bsw/jbe@19 148 if not readonly then
bsw/jbe@19 149 util.help("vote.list", _"Voting")
bsw/jbe@19 150 slot.put('<script src="' .. request.get_relative_baseurl() .. 'static/js/dragdrop.js"></script>')
bsw/jbe@19 151 slot.put('<script src="' .. request.get_relative_baseurl() .. 'static/js/voting.js"></script>')
bsw/jbe@19 152 end
bsw/jbe@19 153
bsw/jbe@19 154 ui.script{
bsw/jbe@19 155 script = function()
bsw/jbe@19 156 slot.put(
bsw/jbe@19 157 "voting_text_approval_single = ", encode.json(_"Approval [single entry]"), ";\n",
bsw/jbe@19 158 "voting_text_approval_multi = ", encode.json(_"Approval [many entries]"), ";\n",
bsw/jbe@19 159 "voting_text_first_preference_single = ", encode.json(_"Approval (first preference) [single entry]"), ";\n",
bsw/jbe@19 160 "voting_text_first_preference_multi = ", encode.json(_"Approval (first preference) [many entries]"), ";\n",
bsw/jbe@19 161 "voting_text_second_preference_single = ", encode.json(_"Approval (second preference) [single entry]"), ";\n",
bsw/jbe@19 162 "voting_text_second_preference_multi = ", encode.json(_"Approval (second preference) [many entries]"), ";\n",
bsw/jbe@19 163 "voting_text_third_preference_single = ", encode.json(_"Approval (third preference) [single entry]"), ";\n",
bsw/jbe@19 164 "voting_text_third_preference_multi = ", encode.json(_"Approval (third preference) [many entries]"), ";\n",
bsw/jbe@19 165 "voting_text_numeric_preference_single = ", encode.json(_"Approval (#th preference) [single entry]"), ";\n",
bsw/jbe@19 166 "voting_text_numeric_preference_multi = ", encode.json(_"Approval (#th preference) [many entries]"), ";\n",
bsw/jbe@19 167 "voting_text_abstention_single = ", encode.json(_"Abstention [single entry]"), ";\n",
bsw/jbe@19 168 "voting_text_abstention_multi = ", encode.json(_"Abstention [many entries]"), ";\n",
bsw/jbe@19 169 "voting_text_disapproval_above_one_single = ", encode.json(_"Disapproval (prefer to lower block) [single entry]"), ";\n",
bsw/jbe@19 170 "voting_text_disapproval_above_one_multi = ", encode.json(_"Disapproval (prefer to lower block) [many entries]"), ";\n",
bsw/jbe@19 171 "voting_text_disapproval_above_many_single = ", encode.json(_"Disapproval (prefer to lower blocks) [single entry]"), ";\n",
bsw/jbe@19 172 "voting_text_disapproval_above_many_multi = ", encode.json(_"Disapproval (prefer to lower blocks) [many entries]"), ";\n",
bsw/jbe@19 173 "voting_text_disapproval_above_last_single = ", encode.json(_"Disapproval (prefer to last block) [single entry]"), ";\n",
bsw/jbe@19 174 "voting_text_disapproval_above_last_multi = ", encode.json(_"Disapproval (prefer to last block) [many entries]"), ";\n",
bsw/jbe@19 175 "voting_text_disapproval_single = ", encode.json(_"Disapproval [single entry]"), ";\n",
bsw/jbe@19 176 "voting_text_disapproval_multi = ", encode.json(_"Disapproval [many entries]"), ";\n"
bsw/jbe@19 177 )
bsw/jbe@19 178 end
bsw/jbe@19 179 }
bsw/jbe@5 180
bsw/jbe@5 181 ui.form{
bsw/jbe@19 182 attr = {
bsw/jbe@19 183 id = "voting_form",
bsw/jbe@19 184 class = readonly and "voting_form_readonly" or "voting_form_active"
bsw/jbe@19 185 },
bsw/jbe@5 186 module = "vote",
bsw/jbe@5 187 action = "update",
bsw/jbe@5 188 params = { issue_id = issue.id },
bsw/jbe@5 189 routing = {
bsw/jbe@5 190 default = {
bsw/jbe@5 191 mode = "redirect",
bsw/jbe@5 192 module = "issue",
bsw/jbe@5 193 view = "show",
bsw/jbe@5 194 id = issue.id
bsw/jbe@5 195 }
bsw/jbe@5 196 },
bsw/jbe@5 197 content = function()
bsw/jbe@19 198 if not readonly then
bsw/jbe@19 199 local scoring = param.get("scoring")
bsw/jbe@19 200 if not scoring then
bsw/jbe@19 201 for i, initiative in ipairs(initiatives) do
bsw/jbe@19 202 local vote = initiative.vote
bsw/jbe@19 203 if vote then
bsw/jbe@19 204 tempvotings[initiative.id] = vote.grade
bsw/jbe@19 205 end
bsw/jbe@19 206 end
bsw/jbe@19 207 local tempvotings_list = {}
bsw/jbe@19 208 for key, val in pairs(tempvotings) do
bsw/jbe@19 209 tempvotings_list[#tempvotings_list+1] = tostring(key) .. ":" .. tostring(val)
bsw/jbe@19 210 end
bsw/jbe@19 211 if #tempvotings_list > 0 then
bsw/jbe@19 212 scoring = table.concat(tempvotings_list, ";")
bsw/jbe@19 213 else
bsw/jbe@19 214 scoring = ""
bsw/jbe@19 215 end
bsw/jbe@19 216 end
bsw/jbe@19 217 slot.put('<input type="hidden" name="scoring" value="' .. scoring .. '"/>')
bsw/jbe@19 218 -- TODO abstrahieren
bsw/jbe@19 219 ui.tag{
bsw/jbe@19 220 tag = "input",
bsw/jbe@19 221 attr = {
bsw@86 222 type = "submit",
bsw/jbe@19 223 class = "voting_done",
bsw/jbe@19 224 value = _"Finish voting"
bsw/jbe@19 225 }
bsw/jbe@5 226 }
bsw/jbe@19 227 end
bsw/jbe@5 228 ui.container{
bsw/jbe@5 229 attr = { id = "voting" },
bsw/jbe@5 230 content = function()
bsw/jbe@19 231 local approval_index, disapproval_index = 0, 0
bsw/jbe@5 232 for grade = max_grade, min_grade, -1 do
bsw/jbe@19 233 local entries = sections[grade]
bsw/jbe@5 234 local class
bsw/jbe@5 235 if grade > 0 then
bsw/jbe@5 236 class = "approval"
bsw/jbe@5 237 elseif grade < 0 then
bsw/jbe@5 238 class = "disapproval"
bsw/jbe@5 239 else
bsw/jbe@5 240 class = "abstention"
bsw/jbe@5 241 end
bsw/jbe@19 242 if
bsw/jbe@19 243 #entries > 0 or
bsw/jbe@19 244 (grade == 1 and not approval_used) or
bsw/jbe@19 245 (grade == -1 and not disapproval_used) or
bsw/jbe@19 246 grade == 0
bsw/jbe@19 247 then
bsw/jbe@19 248 ui.container{
bsw/jbe@19 249 attr = { class = class },
bsw/jbe@19 250 content = function()
bsw/jbe@19 251 local heading
bsw/jbe@19 252 if class == "approval" then
bsw/jbe@19 253 approval_used = true
bsw/jbe@19 254 approval_index = approval_index + 1
bsw/jbe@19 255 if approval_count > 1 then
bsw/jbe@19 256 if approval_index == 1 then
bsw/jbe@19 257 if #entries == 1 then
bsw/jbe@19 258 heading = _"Approval (first preference) [single entry]"
bsw/jbe@19 259 else
bsw/jbe@19 260 heading = _"Approval (first preference) [many entries]"
bsw/jbe@19 261 end
bsw/jbe@19 262 elseif approval_index == 2 then
bsw/jbe@19 263 if #entries == 1 then
bsw/jbe@19 264 heading = _"Approval (second preference) [single entry]"
bsw/jbe@19 265 else
bsw/jbe@19 266 heading = _"Approval (second preference) [many entries]"
bsw/jbe@19 267 end
bsw/jbe@19 268 elseif approval_index == 3 then
bsw/jbe@19 269 if #entries == 1 then
bsw/jbe@19 270 heading = _"Approval (third preference) [single entry]"
bsw/jbe@19 271 else
bsw/jbe@19 272 heading = _"Approval (third preference) [many entries]"
bsw/jbe@19 273 end
bsw/jbe@19 274 else
bsw/jbe@19 275 if #entries == 1 then
bsw/jbe@19 276 heading = _"Approval (#th preference) [single entry]"
bsw/jbe@19 277 else
bsw/jbe@19 278 heading = _"Approval (#th preference) [many entries]"
bsw/jbe@19 279 end
bsw/jbe@19 280 end
bsw/jbe@19 281 else
bsw/jbe@19 282 if #entries == 1 then
bsw/jbe@19 283 heading = _"Approval [single entry]"
bsw/jbe@19 284 else
bsw/jbe@19 285 heading = _"Approval [many entries]"
bsw/jbe@19 286 end
bsw/jbe@19 287 end
bsw/jbe@19 288 elseif class == "abstention" then
bsw/jbe@19 289 if #entries == 1 then
bsw/jbe@19 290 heading = _"Abstention [single entry]"
bsw/jbe@19 291 else
bsw/jbe@19 292 heading = _"Abstention [many entries]"
bsw/jbe@19 293 end
bsw/jbe@19 294 elseif class == "disapproval" then
bsw/jbe@19 295 disapproval_used = true
bsw/jbe@19 296 disapproval_index = disapproval_index + 1
bsw/jbe@19 297 if disapproval_count > disapproval_index + 1 then
bsw/jbe@19 298 if #entries == 1 then
bsw/jbe@19 299 heading = _"Disapproval (prefer to lower blocks) [single entry]"
bsw/jbe@19 300 else
bsw/jbe@19 301 heading = _"Disapproval (prefer to lower blocks) [many entries]"
bsw/jbe@19 302 end
bsw/jbe@19 303 elseif disapproval_count == 2 and disapproval_index == 1 then
bsw/jbe@19 304 if #entries == 1 then
bsw/jbe@19 305 heading = _"Disapproval (prefer to lower block) [single entry]"
bsw/jbe@19 306 else
bsw/jbe@19 307 heading = _"Disapproval (prefer to lower block) [many entries]"
bsw/jbe@19 308 end
bsw/jbe@19 309 elseif disapproval_index == disapproval_count - 1 then
bsw/jbe@19 310 if #entries == 1 then
bsw/jbe@19 311 heading = _"Disapproval (prefer to last block) [single entry]"
bsw/jbe@19 312 else
bsw/jbe@19 313 heading = _"Disapproval (prefer to last block) [many entries]"
bsw/jbe@19 314 end
bsw/jbe@19 315 else
bsw/jbe@19 316 if #entries == 1 then
bsw/jbe@19 317 heading = _"Disapproval [single entry]"
bsw/jbe@19 318 else
bsw/jbe@19 319 heading = _"Disapproval [many entries]"
bsw/jbe@6 320 end
bsw/jbe@19 321 end
bsw/jbe@19 322 end
bsw/jbe@19 323 ui.tag {
bsw/jbe@19 324 tag = "div",
bsw/jbe@19 325 attr = { class = "cathead" },
bsw/jbe@19 326 content = heading
bsw/jbe@19 327 }
bsw/jbe@19 328 for i, initiative in ipairs(entries) do
bsw/jbe@19 329 ui.container{
bsw/jbe@19 330 attr = {
bsw/jbe@19 331 class = "movable",
bsw/jbe@19 332 id = "entry_" .. tostring(initiative.id)
bsw/jbe@19 333 },
bsw/jbe@19 334 content = function()
bsw/jbe@19 335 local initiators_selector = initiative:get_reference_selector("initiating_members")
bsw/jbe@19 336 :add_where("accepted")
bsw/jbe@19 337 local initiators = initiators_selector:exec()
bsw/jbe@19 338 local initiator_names = {}
bsw/jbe@19 339 for i, initiator in ipairs(initiators) do
bsw/jbe@19 340 initiator_names[#initiator_names+1] = initiator.name
bsw/jbe@19 341 end
bsw/jbe@19 342 local initiator_names_string = table.concat(initiator_names, ", ")
bsw/jbe@19 343 ui.container{
bsw/jbe@19 344 attr = { style = "float: right;" },
bsw/jbe@19 345 content = function()
bsw/jbe@19 346 ui.link{
bsw/jbe@19 347 attr = { class = "clickable" },
bsw/jbe@19 348 content = _"Show",
bsw/jbe@19 349 module = "initiative",
bsw/jbe@19 350 view = "show",
bsw/jbe@19 351 id = initiative.id
bsw/jbe@19 352 }
bsw/jbe@19 353 slot.put(" ")
bsw/jbe@19 354 ui.link{
bsw/jbe@19 355 attr = { class = "clickable", target = "_blank" },
bsw/jbe@19 356 content = _"(new window)",
bsw/jbe@19 357 module = "initiative",
bsw/jbe@19 358 view = "show",
bsw/jbe@19 359 id = initiative.id
bsw/jbe@19 360 }
bsw/jbe@19 361 if not readonly then
bsw/jbe@19 362 slot.put(" ")
bsw/jbe@19 363 ui.image{ attr = { class = "grabber" }, static = "icons/grabber.png" }
bsw/jbe@19 364 end
bsw/jbe@19 365 end
bsw/jbe@19 366 }
bsw/jbe@19 367 if not readonly then
bsw/jbe@19 368 ui.container{
bsw/jbe@19 369 attr = { style = "float: left;" },
bsw/jbe@19 370 content = function()
bsw/jbe@19 371 ui.tag{
bsw/jbe@19 372 tag = "input",
bsw/jbe@19 373 attr = {
bsw/jbe@19 374 onclick = "voting_moveUp(this.parentNode.parentNode); return(false);",
bsw/jbe@19 375 name = "move_up",
bsw/jbe@19 376 value = initiative.id,
bsw/jbe@19 377 class = not disabled and "clickable" or nil,
bsw/jbe@19 378 type = "image",
bsw/jbe@19 379 src = encode.url{ static = "icons/move_up.png" },
bsw/jbe@19 380 alt = _"Move up"
bsw/jbe@19 381 }
bsw/jbe@19 382 }
bsw/jbe@19 383 slot.put("&nbsp;")
bsw/jbe@19 384 ui.tag{
bsw/jbe@19 385 tag = "input",
bsw/jbe@19 386 attr = {
bsw/jbe@19 387 onclick = "voting_moveDown(this.parentNode.parentNode); return(false);",
bsw/jbe@19 388 name = "move_down",
bsw/jbe@19 389 value = initiative.id,
bsw/jbe@19 390 class = not disabled and "clickable" or nil,
bsw/jbe@19 391 type = "image",
bsw/jbe@19 392 src = encode.url{ static = "icons/move_down.png" },
bsw/jbe@19 393 alt = _"Move down"
bsw/jbe@19 394 }
bsw/jbe@19 395 }
bsw/jbe@19 396 slot.put("&nbsp;")
bsw/jbe@19 397 end
bsw/jbe@6 398 }
bsw/jbe@6 399 end
bsw/jbe@6 400 ui.container{
bsw/jbe@19 401 content = function()
bsw/jbe@19 402 slot.put(encode.html(initiative.shortened_name))
bsw/jbe@19 403 if #initiators > 1 then
bsw/jbe@19 404 ui.container{
bsw/jbe@19 405 attr = { style = "font-size: 80%;" },
bsw/jbe@19 406 content = _"Initiators" .. ": " .. initiator_names_string
bsw/jbe@19 407 }
bsw/jbe@19 408 else
bsw/jbe@19 409 ui.container{
bsw/jbe@19 410 attr = { style = "font-size: 80%;" },
bsw/jbe@19 411 content = _"Initiator" .. ": " .. initiator_names_string
bsw/jbe@19 412 }
bsw/jbe@19 413 end
bsw/jbe@19 414 end
bsw/jbe@6 415 }
bsw/jbe@6 416 end
bsw/jbe@19 417 }
bsw/jbe@19 418 end
bsw/jbe@5 419 end
bsw/jbe@19 420 }
bsw/jbe@19 421 end
bsw/jbe@5 422 end
bsw/jbe@5 423 end
bsw/jbe@5 424 }
bsw/jbe@19 425 if not readonly then
bsw/jbe@19 426 ui.tag{
bsw/jbe@19 427 tag = "input",
bsw/jbe@19 428 attr = {
bsw@86 429 type = "submit",
bsw/jbe@19 430 class = "voting_done",
bsw/jbe@19 431 value = _"Finish voting"
bsw/jbe@19 432 }
bsw/jbe@5 433 }
bsw/jbe@19 434 end
bsw/jbe@5 435 end
bsw/jbe@5 436 }
bsw/jbe@5 437
bsw/jbe@5 438

Impressum / About Us