liquid_feedback_frontend
view app/main/timeline/index.lua @ 172:165f4bd02cf3
don't show the first draft of a new initiative as a new draft event in the timeline
new draft should only show changes of drafts drafts of new initiatives as they are handled by the new initiative event
new draft should only show changes of drafts drafts of new initiatives as they are handled by the new initiative event
| author | Daniel Poelzleithner <poelzi@poelzi.org> | 
|---|---|
| date | Sun Oct 10 19:40:32 2010 +0200 (2010-10-10) | 
| parents | 77260f05fd4b | 
| children | 
 line source
     1 execute.view{
     2   module = "timeline",
     3   view = "_constants"
     4 }
     6 local active_name = ""
     7 local areas_ignored = {}
     8 local options_box_count = param.get("options_box_count", atom.number) or 1
     9 if options_box_count > 10 then
    10   options_box_count = 10
    11 end
    13 local function format_dow(dow)
    14   local dows = {
    15     _"Monday",
    16     _"Tuesday",
    17     _"Wednesday",
    18     _"Thursday",
    19     _"Friday",
    20     _"Saturday",
    21     _"Sunday"
    22   }
    23   return dows[dow+1]
    24 end
    25 slot.put_into("title", _"Timeline")
    27 slot.select("actions", function()
    28   local setting_key = "liquidfeedback_frontend_timeline_current_options"
    29   local setting = Setting:by_pk(app.session.member.id, setting_key)
    30   local current_options = ""
    31   if setting then
    32     current_options = setting.value
    33   end
    34   local setting_maps = app.session.member:get_setting_maps_by_key("timeline_filters")
    35   for i, setting_map in ipairs(setting_maps) do
    36     local active
    37     local options_string = setting_map.value
    38     local name = setting_map.subkey
    39     if options_string == current_options then
    40       active = true
    41       active_name = name
    42     end
    43     ui.link{
    44       image  = { static = "icons/16/time.png" },
    45       attr   = { class = active and "action_active" or nil },
    46       text   = name,
    47       form_attr = { class = "inline" },
    48       module = 'timeline',
    49       action = 'update',
    50       params = {
    51         options_string = options_string
    52       },
    53     }
    54   end
    55   if #setting_maps > 0 then
    56     ui.link{
    57       content = function()
    58         ui.image{ static = "icons/16/wrench.png" }
    59         slot.put(_"Manage filter")
    60       end,
    61       module = "timeline",
    62       view = "list_filter",
    63     }
    64   end
    65   ui.link{
    66     content = function()
    67       ui.image{ static = "icons/16/bullet_disk.png" }
    68       slot.put(_"Save current filter")
    69     end,
    70     module = "timeline",
    71     view = "save_filter",
    72     params = {
    73       current_name = active_name
    74     },
    75     attr = { 
    76       onclick = "el=document.getElementById('timeline_save');el.checked=true;el.form.submit();return(false);"
    77     }
    78   }
    79 end)
    81 util.help("timeline.index", _"Timeline")
    83 ui.form{
    84   module = "timeline",
    85   action = "update",
    86   content = function()
    87     ui.container{
    89       content = function()
    91         ui.tag{
    92           tag = "input",
    93           attr = {
    94             type = "radio",
    95             id = "timeline_search_last_24h",
    96             name = "search_from",
    97             value = "last_24h",
    98             checked = param.get("date") == "last_24h" and "checked" or nil
    99           },
   100         }
   102         ui.tag{
   103           tag = "label",
   104           attr = {
   105             ["for"] = "timeline_search_last_24h" 
   106           },
   107           content = " " .. _"last 24 hours" .. " "
   108         }
   110         ui.tag{
   111           tag = "input",
   112           attr = {
   113             type = "radio",
   114             id = "timeline_search_from_date",
   115             name = "search_from",
   116             value = "date",
   117             checked = not (param.get("date") == "last_24h") and "checked" or nil
   118           },
   119         }
   121         slot.put(" ")
   122         local current_date = param.get("date")
   123         if not current_date or #current_date == 0 or current_date == "last_24h" then
   124           current_date = tostring(db:query("select now()::date as date")[1].date)
   125         end
   126         ui.tag{
   127           tag = "input",
   128           attr = {
   129             type = "text",
   130             id = "timeline_search_date",
   131             style = "width: 10em;",
   132             onchange = "this.form.submit();",
   133             onclick = "document.getElementById('timeline_search_from_date').checked = true;",
   134             name = "date",
   135             value = current_date
   136           },
   137           content = function() end
   138         }
   140         ui.script{ static = "gregor.js/gregor.js" }
   141         util.gregor("timeline_search_date", true)
   144         ui.link{
   145           attr = { style = "margin-left: 1em; font-weight: bold;", onclick = "document.getElementById('timeline_search_date').form.submit();return(false);" },
   146           content = function()
   147             ui.image{
   148               attr = { style = "margin-right: 0.25em;" },
   149               static = "icons/16/magnifier.png"
   150             }
   151             slot.put(_"Search")
   152           end,
   153           external = "#",
   154         }
   155         local show_options = param.get("show_options", atom.boolean)
   156         ui.link{
   157           attr = { style = "margin-left: 1em;", onclick = "el=document.getElementById('timeline_show_options');el.checked=" .. tostring(not show_options) .. ";el.form.submit();return(false);" },
   158           content = function()
   159             ui.image{
   160               attr = { style = "margin-right: 0.25em;" },
   161               static = "icons/16/text_list_bullets.png"
   162             }
   163             slot.put(not show_options and _"Show filter details" or _"Hide filter details")
   164           end,
   165           external = "#",
   166         }
   168         ui.field.boolean{
   169           attr = { id = "timeline_show_options", style = "display: none;", onchange="this.form.submit();" },
   170           name = "show_options",
   171           value = param.get("show_options", atom.boolean)
   172         }
   173         ui.hidden_field{ name = "current_name", value = active_name }
   174         ui.field.boolean{
   175           attr = { id = "timeline_save", style = "display: none;", onchange="this.form.submit();" },
   176           name = "save",
   177           value = false
   178         }
   180       end
   181     }
   183     ui.container{
   184       attr = { 
   185         id = "timeline_options_boxes",
   186         class = "vertical",
   187         style = not param.get("show_options", atom.boolean) and "display: none;" or nil
   188       },
   189       content = function()
   191         local function option_field(event_ident, filter_ident)
   192           local param_name
   193           if not filter_ident then
   194             param_name = "option_" .. event_ident
   195           else
   196             param_name = "option_" .. event_ident .. "_" .. filter_ident
   197           end
   198           local value = param.get(param_name, atom.boolean)
   199           ui.field.boolean{
   200             attr = { id = param_name },
   201             name = param_name,
   202             value = value,
   203           }
   204         end
   206         local function filter_option_fields(event_ident, filter_idents)
   208           for i, filter_ident in ipairs(filter_idents) do
   209               slot.put("<td>")
   210               option_field(event_ident, filter_ident)
   211               slot.put("</td><td><div class='ui_field_label label_right'>")
   212               ui.tag{
   213                 attr = { ["for"] = "option_" .. event_ident .. "_" .. filter_ident },
   214                 tag = "label",
   215                 content = filter_names[filter_ident]
   216               }
   217               slot.put("</div></td>")
   218           end
   220         end
   222         local event_groups = {
   223           {
   224             title = _"Issue events",
   225             event_idents = {
   226               "issue_created",
   227               "issue_canceled",
   228               "issue_accepted",
   229               "issue_half_frozen",
   230               "issue_finished_without_voting",
   231               "issue_voting_started",
   232               "issue_finished_after_voting",
   233             },
   234             filter_idents = {
   235               "membership",
   236               "interested"
   237             }
   238           },
   239           {
   240             title = _"Initiative events",
   241             event_idents = {
   242               "initiative_created",
   243               "initiative_revoked",
   244               "draft_created",
   245               "suggestion_created",
   246             },
   247             filter_idents = {
   248               "membership",
   249               "interested",
   250               "supporter",
   251               "potential_supporter",
   252               "initiator"
   253             }
   254           }
   255         }
   257         slot.put("<table>")
   259         for i_event_group, event_group in ipairs(event_groups) do
   260           slot.put("<tr>")
   261           slot.put("<th colspan='2'>")
   262           slot.put(event_group.title)
   263           slot.put("</th><th colspan='10'>")
   264           slot.put(_"Show only events which match... (or associtated)")
   265           slot.put("</th>")
   266           slot.put("</tr>")
   267           local event_idents = event_group.event_idents
   268           for i, event_ident in ipairs(event_idents) do
   269             slot.put("<tr><td>")
   270             option_field(event_ident)
   271             slot.put("</td><td><div class='ui_field_label label_right'>")
   272             ui.tag{
   273               attr = { ["for"] = "option_" .. event_ident },
   274               tag = "label",
   275               content = event_names[event_ident]
   276             }
   277             slot.put("</div></td>")
   278             filter_option_fields(event_ident, event_group.filter_idents)
   279             slot.put("</tr>")
   280           end
   281         end
   283         slot.put("</table>")
   285         local areas = Area:new_selector():add_where("active='t'"):exec()
   286         for i, area in ipairs(areas) do
   287           if param.get("option_ignore_area_"..tostring(area.id)) then
   288             areas_ignored[#areas_ignored+1] = area.id
   289           end
   290         end
   292         ui.multiselect{
   293           style = "checkbox",
   294           selected_ids = areas_ignored, 
   295           container_attr = { class = "ignore_area_list" },
   296           container2_attr = { class = "ignore_area_item" },
   297           label = _"Ignore Areas",
   298           name = "option_ignore_area[]",
   299           foreign_records = areas,
   300           foreign_id      = "id",
   301           foreign_name    = "name"
   302         }
   304       end
   305     }
   306   end
   307 }
   309 local date = param.get("date")
   311 if not date or #date == 0 then
   312   date = "today"
   313 end
   315 local timeline_selector
   317 for event, event_name in pairs(event_names) do
   319   if param.get("option_" .. event, atom.boolean) then
   321     local tmp = Timeline:new_selector()
   322       if event == "draft_created" then
   323         tmp
   324           :reset_fields()
   325           :add_field("max(timeline.occurrence)", "occurrence")
   326           :add_field("timeline.event", nil,  { "grouped" })
   327           :add_field("timeline.issue_id", nil, { "grouped" })
   328           :add_field("draft.initiative_id", nil, { "grouped" })
   329           :add_field("max(timeline.draft_id)", "draft_id")
   330           :add_field("timeline.suggestion_id", nil, { "grouped" })
   331           :add_field("COUNT(*)", "count")
   332       else
   333         tmp
   334           :add_field("1", "count")
   335       end
   337       if date == "last_24h" then
   338         tmp:add_where{ "occurrence > now() - '24 hours'::interval" }
   339       else
   340         local start,stop = string.gmatch(date, "(%d+-%d+-%d+):(%d+-%d+-%d+)")()
   341         if start and stop then
   342           tmp:add_where{ "occurrence::date >= ?::date AND occurrence::date <= ?::date", start, stop }
   343         else
   344           local age = string.gmatch(date, "age:(.+)")()
   345           if age then
   346             tmp:add_where{ "occurrence >= now() - ?::interval", age }
   347           else 
   348             local since = string.gmatch(date, "since:%s*(%d+-%d+-%d+)%s*")()
   349             if since then
   350               tmp:add_where{ "occurrence::date >= ?::date", since }
   351             else
   352               tmp:add_where{ "occurrence::date = ?::date", date }
   353             end
   354           end
   355         end
   356       end
   357       tmp
   358         :left_join("draft", nil, "draft.id = timeline.draft_id")
   359         :left_join("suggestion", nil, "suggestion.id = timeline.suggestion_id")
   360         :left_join("initiative", nil, "initiative.id = timeline.initiative_id or initiative.id = draft.initiative_id or initiative.id = suggestion.initiative_id")
   361         :left_join("issue", nil, "issue.id = timeline.issue_id or issue.id = initiative.issue_id")
   362         :left_join("area", nil, "area.id = issue.area_id")
   364         :left_join("interest", "_interest", { "_interest.issue_id = issue.id AND _interest.member_id = ?", app.session.member.id} )
   365         :left_join("initiator", "_initiator", { "_initiator.initiative_id = initiative.id AND _initiator.member_id = ?", app.session.member.id} )
   366         :left_join("membership", "_membership", { "_membership.area_id = area.id AND _membership.member_id = ?", app.session.member.id} )
   367         :left_join("supporter", "_supporter", { "_supporter.initiative_id = initiative.id AND _supporter.member_id = ?", app.session.member.id} )
   369       local group
   370       if event == "draft_created" then
   371         group = { "grouped" }
   372         tmp:add_where{"EXISTS(SELECT 1 from draft as cdraft WHERE draft.initiative_id = cdraft.initiative_id AND cdraft.id != draft.id ORDER BY cdraft.id DESC)"}
   373       end
   375       tmp
   376         :add_field("(_interest.member_id NOTNULL)", "is_interested", group)
   377         :add_field("(_initiator.member_id NOTNULL)", "is_initiator", group)
   378         :add_field({"(_supporter.member_id NOTNULL) AND NOT EXISTS(SELECT NULL FROM opinion WHERE opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)) LIMIT 1)", app.session.member.id }, "is_supporter", group)
   379         :add_field({"EXISTS(SELECT NULL FROM opinion WHERE opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)) LIMIT 1)", app.session.member.id }, "is_potential_supporter", group)
   380   --    :left_join("member", nil, "member.id = timeline.member_id", group)
   382       if #areas_ignored > 0 then
   383         tmp:add_where{"area.id NOT IN ($)", areas_ignored}
   384       end
   386     tmp:add_where{ "event = ?", event }
   388     local filters = {}
   389     if param.get("option_" .. event .. "_membership", atom.boolean) then
   390       filters[#filters+1] = "(timeline.initiative_id ISNULL AND timeline.issue_id ISNULL AND timeline.draft_id ISNULL AND timeline.suggestion_id ISNULL) OR _membership.member_id NOTNULL"
   391     end
   393     if param.get("option_" .. event .. "_supporter", atom.boolean) then
   394       filters[#filters+1] = "(timeline.initiative_id ISNULL AND timeline.issue_id ISNULL AND timeline.draft_id ISNULL AND timeline.suggestion_id ISNULL) OR ((_supporter.member_id NOTNULL) AND NOT EXISTS(SELECT NULL FROM opinion WHERE opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)) LIMIT 1))"
   395     end
   397     if param.get("option_" .. event .. "_potential_supporter", atom.boolean) then
   398       filters[#filters+1] = "(timeline.initiative_id ISNULL AND timeline.issue_id ISNULL AND timeline.draft_id ISNULL AND timeline.suggestion_id ISNULL) OR ((_supporter.member_id NOTNULL) AND EXISTS(SELECT NULL FROM opinion WHERE opinion.initiative_id = initiative.id AND opinion.member_id = ? AND ((opinion.degree = 2 AND NOT fulfilled) OR (opinion.degree = -2 AND fulfilled)) LIMIT 1))"
   399     end
   401     if param.get("option_" .. event .. "_interested", atom.boolean) then
   402       filters[#filters+1] = "(timeline.initiative_id ISNULL AND timeline.issue_id ISNULL AND timeline.draft_id ISNULL AND timeline.suggestion_id ISNULL) OR _interest.member_id NOTNULL"
   403     end
   405     if param.get("option_" .. event .. "_initiator", atom.boolean) then
   406       filters[#filters+1] = "(timeline.initiative_id ISNULL AND timeline.issue_id ISNULL AND timeline.draft_id ISNULL AND timeline.suggestion_id ISNULL) OR _initiator.member_id NOTNULL"
   407     end
   409     if #filters > 0 then
   410       local filter_string = "(" .. table.concat(filters, ") OR (") .. ")"
   411       tmp:add_where{ filter_string, app.session.member.id, app.session.member.id }
   412     end
   414     if not timeline_selector then
   415       timeline_selector = tmp
   416     else
   417       timeline_selector:union_all(tmp)
   418     end
   419   end
   420 end
   422 if timeline_selector then
   424   local initiatives_per_page = param.get("initiatives_per_page", atom.number)
   426   local outer_timeline_selector = db:new_selector()
   427   outer_timeline_selector._class = Timeline
   428   outer_timeline_selector
   429     :add_field{ "timeline.*" }
   430     :from({"($)", { timeline_selector }}, "timeline" )
   431     :add_order_by("occurrence DESC")
   433   slot.put("<br />")
   434   execute.view{
   435     module = "timeline",
   436     view = "_list",
   437     params = {
   438       timeline_selector = outer_timeline_selector,
   439       per_page = param.get("per_page", atom.number),
   440       event_names = event_names,
   441       initiatives_per_page = initiatives_per_page
   442     }
   443   }
   445 else
   447   slot.put(_"No events selected to list")
   449 end
