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)
