liquid_feedback_frontend

annotate app/main/vote/list.lua @ 95:6a12fb7e4963

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

Impressum / About Us