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