liquid_feedback_frontend
view app/main/initiative/show.lua @ 10:72c5e0ee7c98
Version beta6
Bugfixes:
- Security fix: Every user was able to change the discussion URL of an initiative
- Creation of new issues in areas without default policies is now possible
- Members can now be sorted in different ways
- No error when trying to compare a draft with itself
- Added missing local statement to variable initialization in app/main/delegation/new.lua
- CSS flaw in initiative action bar fixed
New features:
- Possiblity to invite other users to become initiator
- Revokation of initiatives implemented
- Number of suggestions, supporters, etc. is shown on corresponding tabs of initiative view
- Members can now be sorted by account creation (default sorting is "newest first")
- Configuration option to create an automatic discussion link for all issues
- First draft of global timeline feature (not accessible via link yet)
- Custom stylesheet URL for users marked as developers
In area listing the number of closed issues is shown too
Renamed "author" field of initiative to "last author"
Removed wrongly included file app/main/member/_show_thumb.lua.orig in the distribution
Help texts updated
Bugfixes:
- Security fix: Every user was able to change the discussion URL of an initiative
- Creation of new issues in areas without default policies is now possible
- Members can now be sorted in different ways
- No error when trying to compare a draft with itself
- Added missing local statement to variable initialization in app/main/delegation/new.lua
- CSS flaw in initiative action bar fixed
New features:
- Possiblity to invite other users to become initiator
- Revokation of initiatives implemented
- Number of suggestions, supporters, etc. is shown on corresponding tabs of initiative view
- Members can now be sorted by account creation (default sorting is "newest first")
- Configuration option to create an automatic discussion link for all issues
- First draft of global timeline feature (not accessible via link yet)
- Custom stylesheet URL for users marked as developers
In area listing the number of closed issues is shown too
Renamed "author" field of initiative to "last author"
Removed wrongly included file app/main/member/_show_thumb.lua.orig in the distribution
Help texts updated
author | bsw |
---|---|
date | Sun Jan 10 12:00:00 2010 +0100 (2010-01-10) |
parents | 8d91bccab0bf |
children | 77d58efe99fd |
line source
1 local initiative = Initiative:new_selector():add_where{ "id = ?", param.get_id()}:single_object_mode():exec()
3 slot.select("actions", function()
4 ui.link{
5 content = function()
6 ui.image{ static = "icons/16/script.png" }
7 slot.put(_"Show all initiatives")
8 end,
9 module = "issue",
10 view = "show",
11 id = initiative.issue.id
12 }
13 end)
15 execute.view{
16 module = "issue",
17 view = "_show_head",
18 params = { issue = initiative.issue }
19 }
21 if initiative.revoked then
22 ui.container{
23 attr = { class = "revoked_info" },
24 content = function()
25 slot.put(_("This initiative has been revoked at #{revoked}", { revoked = format.timestamp(initiative.revoked) }))
26 local suggested_initiative = initiative.suggested_initiative
27 if suggested_initiative then
28 slot.put("<br /><br />")
29 slot.put(_("The initiators suggest to support the following initiative:"))
30 slot.put("<br />")
31 ui.link{
32 content = _("Issue ##{id}", { id = suggested_initiative.issue.id } ) .. ": " .. encode.html(suggested_initiative.name),
33 module = "initiative",
34 view = "show",
35 id = suggested_initiative.id
36 }
37 end
38 end
39 }
40 end
42 local initiator = Initiator:by_pk(initiative.id, app.session.member.id)
44 --slot.put_into("html_head", '<link rel="alternate" type="application/rss+xml" title="RSS" href="../show/' .. tostring(initiative.id) .. '.rss" />')
47 slot.select("actions", function()
48 if not initiative.issue.fully_frozen and not initiative.issue.closed then
49 ui.link{
50 attr = { class = "action" },
51 content = function()
52 ui.image{ static = "icons/16/script_add.png" }
53 slot.put(_"Create alternative initiative")
54 end,
55 module = "initiative",
56 view = "new",
57 params = { issue_id = initiative.issue.id }
58 }
59 end
60 end)
62 slot.put_into("sub_title", encode.html(_"Initiative: '#{name}'":gsub("#{name}", initiative.shortened_name) ))
64 slot.select("support", function()
65 ui.container{
66 attr = { class = "actions" },
67 content = function()
68 execute.view{
69 module = "supporter",
70 view = "_show_box",
71 params = { initiative = initiative }
72 }
73 if initiator and initiator.accepted and not initiative.issue.fully_frozen and not initiative.issue.closed and not initiative.revoked then
74 ui.link{
75 attr = { class = "action", style = "float: left;" },
76 content = function()
77 ui.image{ static = "icons/16/script_delete.png" }
78 slot.put(_"Revoke initiative")
79 end,
80 module = "initiative",
81 view = "revoke",
82 id = initiative.id
83 }
84 end
85 end
86 }
87 end)
89 util.help("initiative.show")
91 if initiator and initiator.accepted == nil then
92 ui.container{
93 attr = { class = "initiator_invite_info" },
94 content = function()
95 slot.put(_"You are invited to become initiator of this initiative.")
96 slot.put(" ")
97 ui.link{
98 content = function()
99 ui.image{ static = "icons/16/tick.png" }
100 slot.put(_"Accept invitation")
101 end,
102 module = "initiative",
103 action = "accept_invitation",
104 id = initiative.id,
105 routing = {
106 default = {
107 mode = "redirect",
108 module = request.get_module(),
109 view = request.get_view(),
110 id = param.get_id_cgi(),
111 params = param.get_all_cgi()
112 }
113 }
114 }
115 slot.put(" ")
116 ui.link{
117 content = function()
118 ui.image{ static = "icons/16/cross.png" }
119 slot.put(_"Refuse invitation")
120 end,
121 module = "initiative",
122 action = "reject_initiator_invitation",
123 params = {
124 initiative_id = initiative.id,
125 member_id = app.session.member.id
126 },
127 routing = {
128 default = {
129 mode = "redirect",
130 module = request.get_module(),
131 view = request.get_view(),
132 id = param.get_id_cgi(),
133 params = param.get_all_cgi()
134 }
135 }
136 }
137 end
138 }
139 slot.put("<br />")
140 end
142 if (initiative.discussion_url and #initiative.discussion_url > 0)
143 or (initiator and initiator.accepted and not initiative.issue.half_frozen and not initiative.issue.closed and not initiative.revoked) then
144 ui.container{
145 attr = { class = "vertical" },
146 content = function()
147 ui.container{
148 attr = { class = "ui_field_label" },
149 content = _"Discussion with initiators"
150 }
151 ui.tag{
152 tag = "span",
153 content = function()
154 if initiative.discussion_url and #initiative.discussion_url > 0 then
155 ui.link{
156 attr = {
157 class = "actions",
158 target = "_blank",
159 title = initiative.discussion_url
160 },
161 content = function()
162 slot.put(encode.html(initiative.discussion_url))
163 end,
164 external = initiative.discussion_url
165 }
166 end
167 slot.put(" ")
168 if initiator and initiator.accepted and not initiative.issue.half_frozen and not initiative.issue.closed and not initiative.revoked then
169 ui.link{
170 attr = { class = "actions" },
171 content = _"(change URL)",
172 module = "initiative",
173 view = "edit",
174 id = initiative.id
175 }
176 end
177 end
178 }
179 end
180 }
181 end
184 ui.container{
185 attr = { id = "add_suggestion_form", class = "hidden_inline_form" },
186 content = function()
188 ui.link{
189 content = _"Close",
190 attr = {
191 onclick = "document.getElementById('add_suggestion_form').style.display='none';return(false)",
192 style = "float: right;"
193 }
194 }
196 ui.field.text{ attr = { class = "head" }, value = _"Add new suggestion" }
199 ui.form{
200 module = "suggestion",
201 action = "add",
202 params = { initiative_id = initiative.id },
203 routing = {
204 default = {
205 mode = "redirect",
206 module = "initiative",
207 view = "show",
208 id = initiative.id,
209 params = { tab = "suggestion" }
210 }
211 },
212 attr = { class = "vertical" },
213 content = function()
214 local supported = Supporter:by_pk(initiative.id, app.session.member.id) and true or false
215 if not supported then
216 ui.field.text{
217 attr = { class = "warning" },
218 value = _"You are currently not supporting this initiative. By adding suggestions to this initiative you will automatically become a potential supporter."
219 }
220 end
221 ui.field.text{ label = _"Title (80 chars max)", name = "name" }
222 ui.field.text{ label = _"Description", name = "description", multiline = true }
223 ui.field.select{
224 label = _"Degree",
225 name = "degree",
226 foreign_records = {
227 { id = 1, name = _"should"},
228 { id = 2, name = _"must"},
229 },
230 foreign_id = "id",
231 foreign_name = "name"
232 }
233 ui.submit{ text = _"Commit suggestion" }
234 end
235 }
236 end
237 }
239 local supporter = app.session.member:get_reference_selector("supporters")
240 :add_where{ "initiative_id = ?", initiative.id }
241 :optional_object_mode()
242 :exec()
244 if supporter then
245 local old_draft_id = supporter.draft_id
246 local new_draft_id = initiative.current_draft.id
247 if old_draft_id ~= new_draft_id then
248 ui.container{
249 attr = { class = "draft_updated_info" },
250 content = function()
251 slot.put("The draft of this initiative has been updated!")
252 slot.put(" ")
253 ui.link{
254 content = _"Show diff",
255 module = "draft",
256 view = "diff",
257 params = {
258 old_draft_id = old_draft_id,
259 new_draft_id = new_draft_id
260 }
261 }
262 slot.put(" ")
263 ui.link{
264 content = _"Refresh support to current draft",
265 module = "initiative",
266 action = "add_support",
267 id = initiative.id,
268 routing = {
269 default = {
270 mode = "redirect",
271 module = "initiative",
272 view = "show",
273 id = initiative.id
274 }
275 }
276 }
277 end
278 }
279 end
280 end
283 local current_draft_name = _"Current draft"
284 if initiative.issue.half_frozen then
285 current_draft_name = _"Voting proposal"
286 end
288 if initiative.issue.state == "finished" then
289 current_draft_name = _"Voted proposal"
290 end
292 local tabs = {
293 {
294 name = "current_draft",
295 label = current_draft_name,
296 content = function()
297 if initiator and initiator.accepted and not initiative.issue.half_frozen and not initiative.issue.closed and not initiative.revoked then
298 ui.link{
299 content = function()
300 ui.image{ static = "icons/16/script_add.png" }
301 slot.put(_"Edit draft")
302 end,
303 module = "draft",
304 view = "new",
305 params = { initiative_id = initiative.id }
306 }
307 end
308 execute.view{ module = "draft", view = "_show", params = { draft = initiative.current_draft } }
309 end
310 }
311 }
313 if initiative.issue.ranks_available then
314 tabs[#tabs+1] = {
315 name = "voter",
316 label = _"Voter",
317 content = function()
318 execute.view{
319 module = "member",
320 view = "_list",
321 params = {
322 initiative = initiative,
323 members_selector = initiative.issue:get_reference_selector("direct_voters")
324 :left_join("vote", nil, { "vote.initiative_id = ? AND vote.member_id = member.id", initiative.id })
325 :add_field("direct_voter.weight as voter_weight")
326 :add_field("coalesce(vote.grade, 0) as grade")
327 }
328 }
329 end
330 }
331 end
333 local suggestion_count = initiative:get_reference_selector("suggestions"):count()
335 tabs[#tabs+1] = {
336 name = "suggestion",
337 label = _"Suggestions" .. " (" .. tostring(suggestion_count) .. ")",
338 content = function()
339 execute.view{
340 module = "suggestion",
341 view = "_list",
342 params = {
343 initiative = initiative,
344 suggestions_selector = initiative:get_reference_selector("suggestions")
345 }
346 }
347 slot.put("<br />")
348 if not initiative.issue.fully_frozen and not initiative.issue.closed and not initiative.revoked then
349 ui.link{
350 content = function()
351 ui.image{ static = "icons/16/comment_add.png" }
352 slot.put(_"Add new suggestion")
353 end,
354 attr = { onclick = "document.getElementById('add_suggestion_form').style.display='block';return(false)" },
355 static = "#"
356 }
357 end
358 end
359 }
361 local members_selector = initiative:get_reference_selector("supporting_members_snapshot")
362 :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id")
363 :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")
364 :add_field("direct_interest_snapshot.weight")
365 :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event")
366 :add_where("direct_supporter_snapshot.satisfied")
368 local satisfied_supporter_count = members_selector:count()
370 tabs[#tabs+1] = {
371 name = "satisfied_supporter",
372 label = _"Supporter" .. " (" .. tostring(satisfied_supporter_count) .. ")",
373 content = function()
374 execute.view{
375 module = "member",
376 view = "_list",
377 params = {
378 initiative = initiative,
379 members_selector = members_selector
380 }
381 }
382 end
383 }
385 local members_selector = initiative:get_reference_selector("supporting_members_snapshot")
386 :join("issue", nil, "issue.id = direct_supporter_snapshot.issue_id")
387 :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")
388 :add_field("direct_interest_snapshot.weight")
389 :add_where("direct_supporter_snapshot.event = issue.latest_snapshot_event")
390 :add_where("NOT direct_supporter_snapshot.satisfied")
392 local potential_supporter_count = members_selector:count()
394 tabs[#tabs+1] = {
395 name = "supporter",
396 label = _"Potential supporter" .. " (" .. tostring(potential_supporter_count) .. ")",
397 content = function()
398 execute.view{
399 module = "member",
400 view = "_list",
401 params = {
402 initiative = initiative,
403 members_selector = members_selector
404 }
405 }
406 end
407 }
409 local initiator_count = initiative:get_reference_selector("initiators"):add_where("accepted"):count()
411 tabs[#tabs+1] = {
412 name = "initiators",
413 label = _"Initiators" .. " (" .. tostring(initiator_count) .. ")",
414 content = function()
415 if initiator and initiator.accepted and not initiative.issue.fully_frozen and not initiative.issue.closed and not initiative.revoked then
416 ui.link{
417 attr = { class = "action" },
418 content = function()
419 ui.image{ static = "icons/16/user_add.png" }
420 slot.put(_"Invite initiator")
421 end,
422 module = "initiative",
423 view = "add_initiator",
424 params = { initiative_id = initiative.id }
425 }
426 if initiator_count > 1 then
427 ui.link{
428 content = function()
429 ui.image{ static = "icons/16/user_delete.png" }
430 slot.put(_"Remove initiator")
431 end,
432 module = "initiative",
433 view = "remove_initiator",
434 params = { initiative_id = initiative.id }
435 }
436 end
437 end
438 if initiator and initiator.accepted == false then
439 ui.link{
440 content = function()
441 ui.image{ static = "icons/16/user_delete.png" }
442 slot.put(_"Cancel refuse of invitation")
443 end,
444 module = "initiative",
445 action = "remove_initiator",
446 params = {
447 initiative_id = initiative.id,
448 member_id = app.session.member.id
449 },
450 routing = {
451 ok = {
452 mode = "redirect",
453 module = "initiative",
454 view = "show",
455 id = initiative.id
456 }
457 }
458 }
459 end
460 local members_selector = initiative:get_reference_selector("initiating_members")
461 :add_field("initiator.accepted", "accepted")
462 if not (initiator and initiator.accepted) then
463 members_selector:add_where("accepted")
464 end
465 execute.view{
466 module = "member",
467 view = "_list",
468 params = {
469 members_selector = members_selector,
470 initiator = initiator
471 }
472 }
473 end
474 }
476 local drafts_count = initiative:get_reference_selector("drafts"):count()
478 tabs[#tabs+1] = {
479 name = "drafts",
480 label = _"Draft history" .. " (" .. tostring(drafts_count) .. ")",
481 content = function()
482 execute.view{ module = "draft", view = "_list", params = { drafts = initiative.drafts } }
483 end
484 }
486 tabs[#tabs+1] = {
487 name = "details",
488 label = _"Details",
489 content = function()
490 ui.form{
491 attr = { class = "vertical" },
492 record = initiative,
493 readonly = true,
494 content = function()
495 ui.field.text{ label = _"Issue policy", value = initiative.issue.policy.name }
496 ui.field.text{
497 label = _"Created at",
498 value = tostring(initiative.created)
499 }
500 ui.field.text{
501 label = _"Created at",
502 value = format.timestamp(initiative.created)
503 }
504 -- ui.field.date{ label = _"Revoked at", name = "revoked" }
505 ui.field.boolean{ label = _"Admitted", name = "admitted" }
506 end
507 }
508 end
509 }
512 ui.tabs(tabs)