liquid_feedback_frontend
view app/main/vote/list.lua @ 1196:5f9cf5a1d12f
Added missing "lf_update_suggestion_order" file to "cp" command in INSTALL.mkd and INSTALL.html
| author | jbe | 
|---|---|
| date | Mon Jun 01 15:11:32 2015 +0200 (2015-06-01) | 
| parents | 904f6807f7fa | 
| 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(" ")
   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(" ")
   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 )
