liquid_feedback_frontend

view app/main/vote/list.lua @ 1145:904f6807f7fa

Added support for upcoming moonbridge bases WebMCP
author bsw
date Sat Mar 21 15:26:39 2015 +0100 (2015-03-21)
parents 701a5cf6b067
children 32cc544d5a5b
line source
1 local issue = Issue:by_id(param.get("issue_id"), atom.integer)
3 local member_id = param.get("member_id", atom.integer)
4 local member
5 local readonly = false
7 local preview = param.get("preview") and true or false
9 if member_id then
10 if not issue.closed then
11 error("access denied")
12 end
13 member = Member:by_id(member_id)
14 readonly = true
15 end
17 if issue.closed then
18 if not member then
19 member = app.session.member
20 end
21 readonly = true
22 end
24 if preview then
25 readonly = true
26 end
28 local submit_button_text = _"Finish voting"
29 local edit_button_text = _"Edit again"
31 if issue.closed then
32 submit_button_text = _"Save voting comment"
33 edit_button_text = _"Edit voting comment"
34 end
36 execute.view {
37 module = "issue", view = "_head", params = { issue = issue }
38 }
40 local direct_voter
42 if member then
43 direct_voter = DirectVoter:by_pk(issue.id, member.id)
44 else
45 member = app.session.member
46 direct_voter = DirectVoter:by_pk(issue.id, member.id)
47 end
51 local tempvoting_string = param.get("scoring")
53 local tempvotings = {}
54 if tempvoting_string then
55 for match in tempvoting_string:gmatch("([^;]+)") do
56 for initiative_id, grade in match:gmatch("([^:;]+):([^:;]+)") do
57 tempvotings[tonumber(initiative_id)] = tonumber(grade)
58 end
59 end
60 end
62 local initiatives = issue:get_reference_selector("initiatives"):add_where("initiative.admitted"):add_order_by("initiative.satisfied_supporter_count DESC"):exec()
64 local min_grade = -1;
65 local max_grade = 1;
67 for i, initiative in ipairs(initiatives) do
68 -- TODO performance
69 initiative.vote = Vote:by_pk(initiative.id, member.id)
70 if tempvotings[initiative.id] then
71 initiative.vote = {}
72 initiative.vote.grade = tempvotings[initiative.id]
73 end
74 if initiative.vote then
75 if initiative.vote.grade > max_grade then
76 max_grade = initiative.vote.grade
77 end
78 if initiative.vote.grade < min_grade then
79 min_grade = initiative.vote.grade
80 end
81 end
82 end
84 local sections = {}
85 for i = min_grade, max_grade do
86 sections[i] = {}
87 for j, initiative in ipairs(initiatives) do
88 if (initiative.vote and initiative.vote.grade == i) or (not initiative.vote and i == 0) then
89 sections[i][#(sections[i])+1] = initiative
90 end
91 end
92 end
94 local approval_count, disapproval_count = 0, 0
95 for i = min_grade, -1 do
96 if #sections[i] > 0 then
97 disapproval_count = disapproval_count + 1
98 end
99 end
100 local approval_count = 0
101 for i = 1, max_grade do
102 if #sections[i] > 0 then
103 approval_count = approval_count + 1
104 end
105 end
107 if not readonly then
108 slot.put('<script src="' .. request.get_relative_baseurl() .. 'static/js/dragdrop.js"></script>')
109 slot.put('<script src="' .. request.get_relative_baseurl() .. 'static/js/voting.js"></script>')
110 end
112 ui.script{
113 script = function()
114 slot.put(
115 "voting_text_approval_single = ", encode.json(_"Approval [single entry]"), ";\n",
116 "voting_text_approval_multi = ", encode.json(_"Approval [many entries]"), ";\n",
117 "voting_text_first_preference_single = ", encode.json(_"Approval (first preference) [single entry]"), ";\n",
118 "voting_text_first_preference_multi = ", encode.json(_"Approval (first preference) [many entries]"), ";\n",
119 "voting_text_second_preference_single = ", encode.json(_"Approval (second preference) [single entry]"), ";\n",
120 "voting_text_second_preference_multi = ", encode.json(_"Approval (second preference) [many entries]"), ";\n",
121 "voting_text_third_preference_single = ", encode.json(_"Approval (third preference) [single entry]"), ";\n",
122 "voting_text_third_preference_multi = ", encode.json(_"Approval (third preference) [many entries]"), ";\n",
123 "voting_text_numeric_preference_single = ", encode.json(_"Approval (#th preference) [single entry]"), ";\n",
124 "voting_text_numeric_preference_multi = ", encode.json(_"Approval (#th preference) [many entries]"), ";\n",
125 "voting_text_abstention_single = ", encode.json(_"Abstention [single entry]"), ";\n",
126 "voting_text_abstention_multi = ", encode.json(_"Abstention [many entries]"), ";\n",
127 "voting_text_disapproval_above_one_single = ", encode.json(_"Disapproval (prefer to lower block) [single entry]"), ";\n",
128 "voting_text_disapproval_above_one_multi = ", encode.json(_"Disapproval (prefer to lower block) [many entries]"), ";\n",
129 "voting_text_disapproval_above_many_single = ", encode.json(_"Disapproval (prefer to lower blocks) [single entry]"), ";\n",
130 "voting_text_disapproval_above_many_multi = ", encode.json(_"Disapproval (prefer to lower blocks) [many entries]"), ";\n",
131 "voting_text_disapproval_above_last_single = ", encode.json(_"Disapproval (prefer to last block) [single entry]"), ";\n",
132 "voting_text_disapproval_above_last_multi = ", encode.json(_"Disapproval (prefer to last block) [many entries]"), ";\n",
133 "voting_text_disapproval_single = ", encode.json(_"Disapproval [single entry]"), ";\n",
134 "voting_text_disapproval_multi = ", encode.json(_"Disapproval [many entries]"), ";\n"
135 )
136 end
137 }
139 if issue.state == "finished_with_winner"
140 or issue.state == "finished_without_winner"
141 then
143 local members_selector = Member:new_selector()
144 :join("delegating_voter", nil, "delegating_voter.member_id = member.id")
145 :add_where{ "delegating_voter.issue_id = ?", issue.id }
146 :add_where{ "delegating_voter.delegate_member_ids[1] = ?", member.id }
147 :add_field("delegating_voter.weight", "voter_weight")
148 :join("issue", nil, "issue.id = delegating_voter.issue_id")
150 ui.sidebar( "tab-members", function()
151 ui.sidebarHead(function()
152 ui.heading{ level = 2, content = _"Incoming delegations" }
153 end)
154 execute.view{
155 module = "member",
156 view = "_list",
157 params = {
158 members_selector = members_selector,
159 trustee = member,
160 issue = issue,
161 initiative = initiative,
162 for_votes = true, no_filter = true,
163 member_class = "sidebarRow sidebarRowNarrow",
164 }
165 }
166 end)
167 end
170 ui.section( function()
172 ui.sectionHead( function()
173 if preview then
174 ui.heading { level = 1, content = _"Preview of voting ballot" }
175 elseif readonly then
176 local str = _("Ballot of '#{member_name}'",
177 {member_name = string.format('<a href="%s">%s</a>',
178 encode.url{
179 module = "member",
180 view = "show",
181 id = member.id,
182 },
183 encode.html(member.name))
184 }
185 )
186 ui.heading { level = 1, content = function () slot.put ( str ) end }
187 else
188 ui.heading { level = 1, content = _"Voting" }
189 end
190 end )
192 ui.sectionRow( function()
194 ui.form{
195 record = direct_voter,
196 attr = {
197 id = "voting_form",
198 class = readonly and "voting_form_readonly" or "voting_form_active"
199 },
200 module = "vote",
201 action = "update",
202 params = { issue_id = issue.id },
203 content = function()
204 if not readonly or preview then
205 local scoring = param.get("scoring")
206 if not scoring then
207 for i, initiative in ipairs(initiatives) do
208 local vote = initiative.vote
209 if vote then
210 tempvotings[initiative.id] = vote.grade
211 else
212 tempvotings[initiative.id] = 0
213 end
214 end
215 local tempvotings_list = {}
216 for key, val in pairs(tempvotings) do
217 tempvotings_list[#tempvotings_list+1] = tostring(key) .. ":" .. tostring(val)
218 end
219 if #tempvotings_list > 0 then
220 scoring = table.concat(tempvotings_list, ";")
221 else
222 scoring = ""
223 end
224 end
225 slot.put('<input type="hidden" name="scoring" value="' .. scoring .. '"/>')
226 end
227 if preview then
228 ui.heading{ level = 2, content = _"Your choice" }
229 elseif not readonly then
230 ui.heading{ level = 2, content = _"Make your choice by placing the initiatives" }
231 end
233 ui.container{
234 attr = { id = "voting" },
235 content = function()
236 local approval_index, disapproval_index = 0, 0
237 local approval_used, disapproval_used
238 for grade = max_grade, min_grade, -1 do
239 local entries = sections[grade]
240 local class
241 if grade > 0 then
242 class = "approval"
243 elseif grade < 0 then
244 class = "disapproval"
245 else
246 class = "abstention"
247 end
248 if
249 #entries > 0 or
250 (grade == 1 and not approval_used) or
251 (grade == -1 and not disapproval_used) or
252 grade == 0
253 then
254 ui.container{
255 attr = { class = class },
256 content = function()
257 local heading
258 if class == "approval" then
259 approval_used = true
260 approval_index = approval_index + 1
261 if approval_count > 1 then
262 if approval_index == 1 then
263 if #entries == 1 then
264 heading = _"Approval (first preference) [single entry]"
265 else
266 heading = _"Approval (first preference) [many entries]"
267 end
268 elseif approval_index == 2 then
269 if #entries == 1 then
270 heading = _"Approval (second preference) [single entry]"
271 else
272 heading = _"Approval (second preference) [many entries]"
273 end
274 elseif approval_index == 3 then
275 if #entries == 1 then
276 heading = _"Approval (third preference) [single entry]"
277 else
278 heading = _"Approval (third preference) [many entries]"
279 end
280 else
281 if #entries == 1 then
282 heading = _"Approval (#th preference) [single entry]"
283 else
284 heading = _"Approval (#th preference) [many entries]"
285 end
286 end
287 else
288 if #entries == 1 then
289 heading = _"Approval [single entry]"
290 else
291 heading = _"Approval [many entries]"
292 end
293 end
294 elseif class == "abstention" then
295 if #entries == 1 then
296 heading = _"Abstention [single entry]"
297 else
298 heading = _"Abstention [many entries]"
299 end
300 elseif class == "disapproval" then
301 disapproval_used = true
302 disapproval_index = disapproval_index + 1
303 if disapproval_count > disapproval_index + 1 then
304 if #entries == 1 then
305 heading = _"Disapproval (prefer to lower blocks) [single entry]"
306 else
307 heading = _"Disapproval (prefer to lower blocks) [many entries]"
308 end
309 elseif disapproval_count == 2 and disapproval_index == 1 then
310 if #entries == 1 then
311 heading = _"Disapproval (prefer to lower block) [single entry]"
312 else
313 heading = _"Disapproval (prefer to lower block) [many entries]"
314 end
315 elseif disapproval_index == disapproval_count - 1 then
316 if #entries == 1 then
317 heading = _"Disapproval (prefer to last block) [single entry]"
318 else
319 heading = _"Disapproval (prefer to last block) [many entries]"
320 end
321 else
322 if #entries == 1 then
323 heading = _"Disapproval [single entry]"
324 else
325 heading = _"Disapproval [many entries]"
326 end
327 end
328 end
329 ui.tag {
330 tag = "div",
331 attr = { class = "cathead" },
332 content = heading
333 }
334 for i, initiative in ipairs(entries) do
335 ui.container{
336 attr = {
337 class = "movable",
338 id = "entry_" .. tostring(initiative.id)
339 },
340 content = function()
341 local initiators_selector = initiative:get_reference_selector("initiating_members")
342 :add_where("accepted")
343 local initiators = initiators_selector:exec()
344 local initiator_names = {}
345 for i, initiator in ipairs(initiators) do
346 initiator_names[#initiator_names+1] = initiator.name
347 end
348 local initiator_names_string = table.concat(initiator_names, ", ")
349 ui.container{
350 attr = { style = "float: right; position: relative;" },
351 content = function()
352 ui.link{
353 attr = { class = "clickable" },
354 content = _"Show",
355 module = "initiative",
356 view = "show",
357 id = initiative.id
358 }
359 slot.put(" ")
360 ui.link{
361 attr = { class = "clickable", target = "_blank" },
362 content = _"(new window)",
363 module = "initiative",
364 view = "show",
365 id = initiative.id
366 }
367 if not readonly then
368 slot.put(" ")
369 ui.image{ attr = { class = "grabber" }, static = "icons/grabber.png" }
370 end
371 end
372 }
373 if not readonly then
374 ui.container{
375 attr = { style = "float: left; position: relative;" },
376 content = function()
377 ui.tag{
378 tag = "input",
379 attr = {
380 onclick = "if (jsFail) return true; voting_moveUp(this.parentNode.parentNode); return(false);",
381 name = "move_up_" .. tostring(initiative.id),
382 class = not disabled and "clickable" or nil,
383 type = "image",
384 src = encode.url{ static = "icons/move_up.png" },
385 alt = _"Move up"
386 }
387 }
388 slot.put("&nbsp;")
389 ui.tag{
390 tag = "input",
391 attr = {
392 onclick = "if (jsFail) return true; voting_moveDown(this.parentNode.parentNode); return(false);",
393 name = "move_down_" .. tostring(initiative.id),
394 class = not disabled and "clickable" or nil,
395 type = "image",
396 src = encode.url{ static = "icons/move_down.png" },
397 alt = _"Move down"
398 }
399 }
400 slot.put("&nbsp;")
401 end
402 }
403 end
404 ui.container{
405 content = function()
406 ui.tag{ content = "i" .. initiative.id .. ": " }
407 ui.tag{ content = initiative.shortened_name }
408 slot.put("<br />")
409 for i, initiator in ipairs(initiators) do
410 ui.link{
411 attr = { class = "clickable" },
412 content = function ()
413 execute.view{
414 module = "member_image",
415 view = "_show",
416 params = {
417 member = initiator,
418 image_type = "avatar",
419 show_dummy = true,
420 class = "micro_avatar",
421 popup_text = text
422 }
423 }
424 end,
425 module = "member", view = "show", id = initiator.id
426 }
427 slot.put(" ")
428 ui.tag{ content = initiator.name }
429 slot.put(" ")
430 end
431 end
432 }
433 end
434 }
435 end
436 end
437 }
438 end
439 end
440 end
441 }
442 if app.session.member_id and preview then
443 local formatting_engine = param.get("formatting_engine") or config.enforce_formatting_engine
444 local comment = param.get("comment")
445 if comment and #comment > 0 then
446 local rendered_comment = format.wiki_text(comment, formatting_engine)
447 ui.heading{ level = "2", content = _"Voting comment" }
448 ui.container { attr = { class = "member_statement" }, content = function()
449 slot.put(rendered_comment)
450 end }
451 slot.put("<br />")
452 end
453 end
454 if (readonly or direct_voter and direct_voter.comment) and not preview and not (app.session.member_id == member.id) then
455 local text
456 if direct_voter and direct_voter.comment_changed then
457 text = _("Voting comment (last updated: #{timestamp})", { timestamp = format.timestamp(direct_voter.comment_changed) })
458 elseif direct_voter and direct_voter.comment then
459 text = _"Voting comment"
460 end
461 if text then
462 ui.heading{ level = "2", content = text }
463 end
464 if direct_voter and direct_voter.comment then
465 local rendered_comment = direct_voter:get_content('html')
466 ui.container { attr = { class = "member_statement" }, content = function()
467 slot.put(rendered_comment)
468 end }
469 slot.put("<br />")
470 end
471 end
472 if app.session.member_id and app.session.member_id == member.id then
473 if (not readonly or direct_voter) and not preview then
474 ui.container{ content = function()
475 if not config.enforce_formatting_engine then
476 ui.field.select{
477 label = _"Wiki engine for statement",
478 name = "formatting_engine",
479 foreign_records = config.formatting_engines,
480 attr = {id = "formatting_engine"},
481 foreign_id = "id",
482 foreign_name = "name",
483 value = param.get("formatting_engine") or direct_voter and direct_voter.formatting_engine
484 }
485 end
486 ui.heading { level = 2, content = _"Voting comment (optional)" }
487 ui.field.text{
488 name = "comment",
489 multiline = true,
490 value = param.get("comment") or direct_voter and direct_voter.comment,
491 attr = { style = "height: 10ex; width: 100%;" },
492 }
493 end }
494 end
496 if preview then
497 if not config.enforce_formatting_engine then
498 ui.field.hidden{ name = "formatting_engine", value = param.get("formatting_engine") }
499 end
500 ui.field.hidden{ name = "comment", value = param.get("comment") or direct_voter and direct_voter.comment }
501 end
503 if not readonly or direct_voter or preview then
504 ui.container{ content = function()
505 if preview then
506 slot.put(" ")
507 ui.tag{
508 tag = "input",
509 attr = {
510 type = "submit",
511 class = "btn btn-default",
512 name = issue.closed and "update_comment" or nil,
513 value = submit_button_text -- finish voting / update comment
514 }
515 }
516 end
517 if not preview then
518 ui.tag{
519 tag = "input",
520 attr = {
521 type = "submit",
522 name = "preview",
523 class = "btn btn-default",
524 value = _"Preview",
525 }
526 }
527 else
528 slot.put(" ")
529 ui.tag{
530 tag = "input",
531 attr = {
532 type = "submit",
533 name = "edit",
534 class = "btn-link",
535 value = edit_button_text,
536 }
537 }
538 end
539 end }
540 end
541 end
542 end
543 }
544 slot.put("<br />")
545 ui.link{
546 text = _"Cancel",
547 module = "issue",
548 view = "show",
549 id = issue.id
550 }
551 if direct_voter then
552 slot.put(" | ")
553 ui.link {
554 module = "vote", action = "update",
555 params = {
556 issue_id = issue.id,
557 discard = true
558 },
559 routing = {
560 default = {
561 mode = "redirect",
562 module = "issue",
563 view = "show",
564 id = issue.id
565 }
566 },
567 text = _"Discard my vote"
568 }
569 end
571 end )
572 end )

Impressum / About Us