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