liquid_feedback_frontend
view app/main/vote/list.lua @ 886:2e9d39b53b64
Added support for setting/removing of beeing a non voter
| author | bsw | 
|---|---|
| date | Mon Aug 20 02:39:13 2012 +0200 (2012-08-20) | 
| parents | e81f35cdc088 | 
| children | b865f87ea810 | 
 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 preview = param.get("preview") or param.get("preview2") == "1" and true or false
     8 if member_id then
     9   if not issue.closed then
    10     error("access denied")
    11   end
    12   member = Member:by_id(member_id)
    13   readonly = true
    14 end
    16 if issue.closed then
    17   if not member then
    18     member = app.session.member
    19   end
    20   readonly = true
    21 end
    23 local submit_button_text = _"Finish voting"
    25 if issue.closed then
    26   submit_button_text = _"Update voting comment"
    27 end
    29 local direct_voter
    31 if member then
    32   direct_voter = DirectVoter:by_pk(issue.id, member.id)
    33   local str = _("Ballot of '#{member_name}' for issue ##{issue_id}",
    34                   {member_name = string.format('<a href="%s">%s</a>',
    35                                           encode.url{
    36                                             module    = "member",
    37                                             view      = "show",
    38                                             id        = member.id,
    39                                           },
    40                                           encode.html(member.name)),
    41                    issue_id = string.format('<a href="%s">%s</a>',
    42                                           encode.url{
    43                                             module    = "issue",
    44                                             view      = "show",
    45                                             id        = issue.id,
    46                                           },
    47                                           encode.html(tostring(issue.id)))
    48                   }
    49               )
    50   ui.raw_title(str)
    51 else
    52   member = app.session.member
    54   direct_voter = DirectVoter:by_pk(issue.id, member.id)
    56   ui.title(_"Voting")
    58   ui.actions(function()
    59     ui.link{
    60       text = _"Cancel",
    61       module = "issue",
    62       view = "show",
    63       id = issue.id
    64     }
    65     if direct_voter then
    66       slot.put(" · ")
    67       ui.link{
    68         text = _"Discard voting",
    69         module = "vote",
    70         action = "update",
    71         params = {
    72           issue_id = issue.id,
    73           discard = true
    74         },
    75         routing = {
    76           default = {
    77             mode = "redirect",
    78             module = "issue",
    79             view = "show",
    80             id = issue.id
    81           }
    82         }
    83       }
    84     end
    85   end)
    86 end
    90 local tempvoting_string = param.get("scoring")
    92 local tempvotings = {}
    93 if tempvoting_string then
    94   for match in tempvoting_string:gmatch("([^;]+)") do
    95     for initiative_id, grade in match:gmatch("([^:;]+):([^:;]+)") do
    96       tempvotings[tonumber(initiative_id)] = tonumber(grade)
    97     end
    98   end
    99 end
   101 local initiatives = issue:get_reference_selector("initiatives"):add_where("initiative.admitted"):add_order_by("initiative.satisfied_supporter_count DESC"):exec()
   103 local min_grade = -1;
   104 local max_grade = 1;
   106 for i, initiative in ipairs(initiatives) do
   107   -- TODO performance
   108   initiative.vote = Vote:by_pk(initiative.id, member.id)
   109   if tempvotings[initiative.id] then
   110     initiative.vote = {}
   111     initiative.vote.grade = tempvotings[initiative.id]
   112   end
   113   if initiative.vote then
   114     if initiative.vote.grade > max_grade then
   115       max_grade = initiative.vote.grade
   116     end
   117     if initiative.vote.grade < min_grade then
   118       min_grade = initiative.vote.grade
   119     end
   120   end
   121 end
   123 local sections = {}
   124 for i = min_grade, max_grade do
   125   sections[i] = {}
   126   for j, initiative in ipairs(initiatives) do
   127     if (initiative.vote and initiative.vote.grade == i) or (not initiative.vote and i == 0) then
   128       sections[i][#(sections[i])+1] = initiative
   129     end
   130   end
   131 end
   133 local approval_count, disapproval_count = 0, 0
   134 for i = min_grade, -1 do
   135   if #sections[i] > 0 then
   136     disapproval_count = disapproval_count + 1
   137   end
   138 end
   139 local approval_count = 0
   140 for i = 1, max_grade do
   141   if #sections[i] > 0 then
   142     approval_count = approval_count + 1
   143   end
   144 end
   148 if not readonly then
   149   util.help("vote.list", _"Voting")
   150   slot.put('<script src="' .. request.get_relative_baseurl() .. 'static/js/dragdrop.js"></script>')
   151   slot.put('<script src="' .. request.get_relative_baseurl() .. 'static/js/voting.js"></script>')
   152 end
   154 ui.script{
   155   script = function()
   156     slot.put(
   157       "voting_text_approval_single               = ", encode.json(_"Approval [single entry]"), ";\n",
   158       "voting_text_approval_multi                = ", encode.json(_"Approval [many entries]"), ";\n",
   159       "voting_text_first_preference_single       = ", encode.json(_"Approval (first preference) [single entry]"), ";\n",
   160       "voting_text_first_preference_multi        = ", encode.json(_"Approval (first preference) [many entries]"), ";\n",
   161       "voting_text_second_preference_single      = ", encode.json(_"Approval (second preference) [single entry]"), ";\n",
   162       "voting_text_second_preference_multi       = ", encode.json(_"Approval (second preference) [many entries]"), ";\n",
   163       "voting_text_third_preference_single       = ", encode.json(_"Approval (third preference) [single entry]"), ";\n",
   164       "voting_text_third_preference_multi        = ", encode.json(_"Approval (third preference) [many entries]"), ";\n",
   165       "voting_text_numeric_preference_single     = ", encode.json(_"Approval (#th preference) [single entry]"), ";\n",
   166       "voting_text_numeric_preference_multi      = ", encode.json(_"Approval (#th preference) [many entries]"), ";\n",
   167       "voting_text_abstention_single             = ", encode.json(_"Abstention [single entry]"), ";\n",
   168       "voting_text_abstention_multi              = ", encode.json(_"Abstention [many entries]"), ";\n",
   169       "voting_text_disapproval_above_one_single  = ", encode.json(_"Disapproval (prefer to lower block) [single entry]"), ";\n",
   170       "voting_text_disapproval_above_one_multi   = ", encode.json(_"Disapproval (prefer to lower block) [many entries]"), ";\n",
   171       "voting_text_disapproval_above_many_single = ", encode.json(_"Disapproval (prefer to lower blocks) [single entry]"), ";\n",
   172       "voting_text_disapproval_above_many_multi  = ", encode.json(_"Disapproval (prefer to lower blocks) [many entries]"), ";\n",
   173       "voting_text_disapproval_above_last_single = ", encode.json(_"Disapproval (prefer to last block) [single entry]"), ";\n",
   174       "voting_text_disapproval_above_last_multi  = ", encode.json(_"Disapproval (prefer to last block) [many entries]"), ";\n",
   175       "voting_text_disapproval_single            = ", encode.json(_"Disapproval [single entry]"), ";\n",
   176       "voting_text_disapproval_multi             = ", encode.json(_"Disapproval [many entries]"), ";\n"
   177     )
   178   end
   179 }
   181 ui.form{
   182   record = direct_voter,
   183   attr = {
   184     id = "voting_form",
   185     class = readonly and "voting_form_readonly" or "voting_form_active"
   186   },
   187   module = "vote",
   188   action = "update",
   189   params = { issue_id = issue.id },
   190   content = function()
   191     if not readonly or preview then
   192       local scoring = param.get("scoring")
   193       if not scoring then
   194         for i, initiative in ipairs(initiatives) do
   195           local vote = initiative.vote
   196           if vote then
   197             tempvotings[initiative.id] = vote.grade
   198           else
   199             tempvotings[initiative.id] = 0
   200           end
   201         end
   202         local tempvotings_list = {}
   203         for key, val in pairs(tempvotings) do
   204           tempvotings_list[#tempvotings_list+1] = tostring(key) .. ":" .. tostring(val)
   205         end
   206         if #tempvotings_list > 0 then
   207           scoring = table.concat(tempvotings_list, ";")
   208         else
   209           scoring = ""
   210         end
   211       end
   212       slot.put('<input type="hidden" name="scoring" value="' .. scoring .. '"/>')
   213       -- TODO abstrahieren
   214       ui.tag{
   215         tag = "input",
   216         attr = {
   217           type = "submit",
   218           class = "voting_done1",
   219           value = submit_button_text
   220         }
   221       }
   222     end
   223     ui.container{
   224       attr = { id = "voting" },
   225       content = function()
   226         local approval_index, disapproval_index = 0, 0
   227         for grade = max_grade, min_grade, -1 do 
   228           local entries = sections[grade]
   229           local class
   230           if grade > 0 then
   231             class = "approval"
   232           elseif grade < 0 then
   233             class = "disapproval"
   234           else
   235             class = "abstention"
   236           end
   237           if
   238             #entries > 0 or
   239             (grade == 1 and not approval_used) or
   240             (grade == -1 and not disapproval_used) or
   241             grade == 0
   242           then
   243             ui.container{
   244               attr = { class = class },
   245               content = function()
   246                 local heading
   247                 if class == "approval" then
   248                   approval_used = true
   249                   approval_index = approval_index + 1
   250                   if approval_count > 1 then
   251                     if approval_index == 1 then
   252                       if #entries == 1 then
   253                         heading = _"Approval (first preference) [single entry]"
   254                       else
   255                         heading = _"Approval (first preference) [many entries]"
   256                       end
   257                     elseif approval_index == 2 then
   258                       if #entries == 1 then
   259                         heading = _"Approval (second preference) [single entry]"
   260                       else
   261                         heading = _"Approval (second preference) [many entries]"
   262                       end
   263                     elseif approval_index == 3 then
   264                       if #entries == 1 then
   265                         heading = _"Approval (third preference) [single entry]"
   266                       else
   267                         heading = _"Approval (third preference) [many entries]"
   268                       end
   269                     else
   270                       if #entries == 1 then
   271                         heading = _"Approval (#th preference) [single entry]"
   272                       else
   273                         heading = _"Approval (#th preference) [many entries]"
   274                       end
   275                     end
   276                   else
   277                     if #entries == 1 then
   278                       heading = _"Approval [single entry]"
   279                     else
   280                       heading = _"Approval [many entries]"
   281                     end
   282                   end
   283                 elseif class == "abstention" then
   284                     if #entries == 1 then
   285                       heading = _"Abstention [single entry]"
   286                     else
   287                       heading = _"Abstention [many entries]"
   288                     end
   289                 elseif class == "disapproval" then
   290                   disapproval_used = true
   291                   disapproval_index = disapproval_index + 1
   292                   if disapproval_count > disapproval_index + 1 then
   293                     if #entries == 1 then
   294                       heading = _"Disapproval (prefer to lower blocks) [single entry]"
   295                     else
   296                       heading = _"Disapproval (prefer to lower blocks) [many entries]"
   297                     end
   298                   elseif disapproval_count == 2 and disapproval_index == 1 then
   299                     if #entries == 1 then
   300                       heading = _"Disapproval (prefer to lower block) [single entry]"
   301                     else
   302                       heading = _"Disapproval (prefer to lower block) [many entries]"
   303                     end
   304                   elseif disapproval_index == disapproval_count - 1 then
   305                     if #entries == 1 then
   306                       heading = _"Disapproval (prefer to last block) [single entry]"
   307                     else
   308                       heading = _"Disapproval (prefer to last block) [many entries]"
   309                     end
   310                   else
   311                     if #entries == 1 then
   312                       heading = _"Disapproval [single entry]"
   313                     else
   314                       heading = _"Disapproval [many entries]"
   315                     end
   316                   end
   317                 end
   318                 ui.tag {
   319                   tag     = "div",
   320                   attr    = { class = "cathead" },
   321                   content = heading
   322                 }
   323                 for i, initiative in ipairs(entries) do
   324                   ui.container{
   325                     attr = {
   326                       class = "movable",
   327                       id = "entry_" .. tostring(initiative.id)
   328                     },
   329                     content = function()
   330                       local initiators_selector = initiative:get_reference_selector("initiating_members")
   331                         :add_where("accepted")
   332                       local initiators = initiators_selector:exec()
   333                       local initiator_names = {}
   334                       for i, initiator in ipairs(initiators) do
   335                         initiator_names[#initiator_names+1] = initiator.name
   336                       end
   337                       local initiator_names_string = table.concat(initiator_names, ", ")
   338                       ui.container{
   339                         attr = { style = "float: right; position: relative;" },
   340                         content = function()
   341                           ui.link{
   342                             attr = { class = "clickable" },
   343                             content = _"Show",
   344                             module = "initiative",
   345                             view = "show",
   346                             id = initiative.id
   347                           }
   348                           slot.put(" ")
   349                           ui.link{
   350                             attr = { class = "clickable", target = "_blank" },
   351                             content = _"(new window)",
   352                             module = "initiative",
   353                             view = "show",
   354                             id = initiative.id
   355                           }
   356                           if not readonly then
   357                             slot.put(" ")
   358                             ui.image{ attr = { class = "grabber" }, static = "icons/grabber.png" }
   359                           end
   360                         end
   361                       }
   362                       if not readonly then
   363                         ui.container{
   364                           attr = { style = "float: left; position: relative;" },
   365                           content = function()
   366                             ui.tag{
   367                               tag = "input",
   368                               attr = {
   369                                 onclick = "if (jsFail) return true; voting_moveUp(this.parentNode.parentNode); return(false);",
   370                                 name = "move_up_" .. tostring(initiative.id),
   371                                 class = not disabled and "clickable" or nil,
   372                                 type = "image",
   373                                 src = encode.url{ static = "icons/move_up.png" },
   374                                 alt = _"Move up"
   375                               }
   376                             }
   377                             slot.put(" ")
   378                             ui.tag{
   379                               tag = "input",
   380                               attr = {
   381                                 onclick = "if (jsFail) return true; voting_moveDown(this.parentNode.parentNode); return(false);",
   382                                 name = "move_down_" .. tostring(initiative.id),
   383                                 class = not disabled and "clickable" or nil,
   384                                 type = "image",
   385                                 src = encode.url{ static = "icons/move_down.png" },
   386                                 alt = _"Move down"
   387                               }
   388                             }
   389                             slot.put(" ")
   390                           end
   391                         }
   392                       end
   393                       ui.container{
   394                         content = function()
   395                           ui.tag{ content = "i" .. initiative.id .. ": " }
   396                           ui.tag{ content = initiative.shortened_name }
   397                           slot.put("<br />")
   398                           for i, initiator in ipairs(initiators) do
   399                             ui.link{
   400                               attr = { class = "clickable" },
   401                               content = function ()
   402                                 execute.view{
   403                                   module = "member_image",
   404                                   view = "_show",
   405                                   params = {
   406                                     member = initiator,
   407                                     image_type = "avatar",
   408                                     show_dummy = true,
   409                                     class = "micro_avatar",
   410                                     popup_text = text
   411                                   }
   412                                 }
   413                               end,
   414                               module = "member", view = "show", id = initiator.id
   415                             }
   416                             slot.put(" ")
   417                             ui.tag{ content = initiator.name }
   418                             slot.put(" ")
   419                           end
   420                         end
   421                       }
   422                     end
   423                   }
   424                 end
   425               end
   426             }
   427           end
   428         end
   429       end
   430     }
   431     if app.session.member_id and preview then
   432       local formatting_engine = param.get("formatting_engine")
   433       local comment = param.get("comment")
   434       local rendered_comment = format.wiki_text(comment, formatting_engine)
   435       slot.put(rendered_comment)
   436     end
   437     if (readonly or direct_voter and direct_voter.comment) and not preview then
   438       local text
   439       if direct_voter and direct_voter.comment_changed then
   440         text = _("Voting comment (last updated: #{timestamp})", { timestamp = format.timestamp(direct_voter.comment_changed) })
   441       elseif direct_voter and direct_voter.comment then
   442         text = _"Voting comment"
   443       end
   444       if text then
   445         ui.heading{ level = "2", content = text }
   446       end
   447       if direct_voter and direct_voter.comment then
   448         local rendered_comment = direct_voter:get_content('html')
   449         ui.container{ attr = { class = "member_statement" }, content = function()
   450           slot.put(rendered_comment)
   451         end }
   452         slot.put("<br />")
   453       end
   454     end
   455     if app.session.member_id and app.session.member_id == member.id then
   456       if not readonly or direct_voter then
   457         ui.field.hidden{ name = "update_comment", value = param.get("update_comment") or issue.closed and "1" }
   458         ui.field.select{
   459           label = _"Wiki engine for statement",
   460           name = "formatting_engine",
   461           foreign_records = {
   462             { id = "rocketwiki", name = "RocketWiki" },
   463             { id = "compat", name = _"Traditional wiki syntax" }
   464           },
   465           attr = {id = "formatting_engine"},
   466           foreign_id = "id",
   467           foreign_name = "name",
   468           value = param.get("formatting_engine") or direct_voter and direct_voter.formatting_engine
   469         }
   470         ui.field.text{
   471           label = _"Voting comment (optional)",
   472           name = "comment",
   473           multiline = true,
   474           value = param.get("comment") or direct_voter and direct_voter.comment,
   475           attr = { style = "height: 20ex;" },
   476         }
   477         ui.field.hidden{ name = "preview2", attr = { id = "preview2" }, value = "0" }
   478         ui.submit{
   479           name = "preview",
   480           value = _"Preview voting comment",
   481           attr = { class = "preview" }
   482         }
   483       end
   484       if not readonly or preview or direct_voter then
   485         slot.put(" ")
   486         ui.tag{
   487           tag = "input",
   488           attr = {
   489             type = "submit",
   490             class = "voting_done2",
   491             value = submit_button_text
   492           }
   493         }
   494       end
   495     end
   496   end
   497 }
