liquid_feedback_frontend

view app/main/issue/_list.lua @ 1858:3d1f0464a3ea

Handle missing ldap.member.allowed function
author bsw
date Tue Sep 20 17:35:29 2022 +0200 (20 months ago)
parents cb2fc0c24e35
children
line source
1 local for_member = param.get ( "for_member", "table" )
2 local for_unit = param.get ( "for_unit", "table" )
3 local for_area = param.get ( "for_area", "table" )
4 local for_issue = param.get ( "for_issue", "table" )
5 local for_initiative = param.get ( "for_initiative", "table" )
6 local for_sidebar = param.get("for_sidebar", atom.boolean)
7 local no_filter = param.get ( "no_filter", atom.boolean )
8 local search = param.get ( "search" )
10 local limit = 25
12 local mode = request.get_param{ name = "mode" } or "issue"
14 if for_initiative or for_issue or for_member then
15 mode = "timeline"
16 end
18 if app.single_unit_id then
19 if request.get_param{ name = "unit" } then
20 for_unit = Unit:by_id(request.get_param{ name = "unit" })
21 else
22 for_unit = Unit:by_id(app.single_unit_id)
23 end
24 end
26 local selector
28 if search then
30 selector = Issue:get_search_selector(search)
33 elseif mode == "timeline" then
35 local event_max_id = request.get_param_strings()["event_max_id"]
37 selector = Event:new_selector()
38 :add_order_by("event.id DESC")
39 :join("issue", nil, "issue.id = event.issue_id")
40 :add_field("now() - event.occurrence", "time_ago")
41 :limit(limit + 1)
43 if event_max_id then
44 selector:add_where{ "event.id < ?", event_max_id }
45 end
47 if for_member then
48 selector:add_where{ "event.member_id = ?", for_member.id }
49 end
51 if for_initiative then
52 selector:add_where{ "event.initiative_id = ?", for_initiative.id }
53 end
56 elseif mode == "issue" then
58 selector = Issue:new_selector()
60 end
62 if for_unit then
63 selector:join("area", nil, "area.id = issue.area_id")
64 selector:add_where{ "area.unit_id = ?", for_unit.id }
65 elseif for_area then
66 selector:add_where{ "issue.area_id = ?", for_area.id }
67 elseif for_issue then
68 selector:add_where{ "issue.id = ?", for_issue.id }
69 end
71 if not search and app.session.member_id then
72 selector
73 :left_join("interest", "_interest", {
74 "_interest.issue_id = issue.id AND _interest.member_id = ?", app.session.member.id
75 } )
76 :add_field("(_interest.member_id NOTNULL)", "is_interested")
77 :left_join("delegating_interest_snapshot", "_delegating_interest", { [[
78 _delegating_interest.issue_id = issue.id AND
79 _delegating_interest.member_id = ? AND
80 _delegating_interest.snapshot_id = issue.latest_snapshot_id
81 ]], app.session.member.id } )
82 :add_field("_delegating_interest.delegate_member_ids[1]", "is_interested_by_delegation_to_member_id")
83 :add_field("_delegating_interest.delegate_member_ids[array_upper(_delegating_interest.delegate_member_ids, 1)]", "is_interested_via_member_id")
84 :add_field("array_length(_delegating_interest.delegate_member_ids, 1)", "delegation_chain_length")
85 end
87 local function doit()
89 local last_event_id
91 local items = selector:exec()
93 local row_class = "sectionRow"
94 if for_sidebar then
95 row_class = "sidebarRow"
96 end
98 if mode == "timeline" then
99 local issues = items:load ( "issue" )
100 local initiative = items:load ( "initiative" )
101 items:load ( "suggestion" )
102 items:load ( "member" )
103 issues:load_everything_for_member_id ( app.session.member_id )
104 initiative:load_everything_for_member_id ( app.session.member_id )
105 elseif mode == "issue" then
106 items:load_everything_for_member_id ( app.session.member_id )
107 end
109 local last_event_date
110 for i, item in ipairs(items) do
111 local event
112 local issue
113 if mode == "timeline" then
114 event = item
115 issue = item.issue
116 elseif mode == "issue" then
117 event = {}
118 issue = item
119 end
121 last_event_id = event.id
123 local class = "mdl-card mdl-shadow--2dp mdl-card__fullwidth event " .. row_class
124 if event.suggestion_id then
125 class = class .. " suggestion"
126 end
128 ui.container{ attr = { class = class, id = "issue_" .. issue.id }, content = function()
129 local event_name
130 local event_icon
131 local negative_event = false
133 local days_ago_text
135 if mode == "timeline" then
136 event_name = event.event_name
137 if event.event == "issue_state_changed" then
138 if event.state == "discussion" then
139 event_name = _"Discussion started"
140 elseif event.state == "verification" then
141 event_name = _"Verification started"
142 elseif event.state == "voting" then
143 event_name = _"Voting started"
144 elseif event.state == "finished_with_winner" then
145 event_name = event.state_name
146 elseif event.state == "finished_without_winner" then
147 event_name = event.state_name
148 negative_event = true
149 else
150 event_name = event.state_name
151 negative_event = true
152 end
153 elseif event.event == "initiative_revoked" then
154 negative_event = true
155 end
157 if event.time_ago == 0 then
158 days_ago_text = _("today at #{time}", { time = format.time(event.occurrence) })
159 elseif event.time_ago == 1 then
160 days_ago_text = _("yesterday at #{time}", { time = format.time(event.occurrence) })
161 else
162 days_ago_text = _("#{interval} ago", { interval = format.interval_text ( event.time_ago ) } )
163 end
165 elseif mode == "issue" then
166 local event_icons_map = {
167 admission = "bubble_chart",
168 discussion = "question_answer",
169 verification = "find_in_page",
170 voting = "mail",
171 finished_with_winner = "emoji_events",
172 finished_without_winner = "do_not_disturb",
173 canceled = "do_not_disturb"
174 }
175 event_icon = event_icons_map[issue.state] or event_icons_map["canceled"]
176 event_name = issue.state_name
177 if issue.state_time_left:sub(1,1) ~= "-" then
178 days_ago_text = _( "#{interval_text} left", {
179 interval_text = format.interval_text ( issue.state_time_left )
180 })
181 elseif issue.closed then
182 days_ago_text = _( "#{interval_text} ago", {
183 interval_text = format.interval_text ( issue.closed_ago )
184 })
185 else
186 days_ago_text = _"phase ends soon"
187 end
188 if issue.closed and not issue.fully_frozen then
189 negative_event = true
190 end
191 if issue.state == "finished_without_winner"
192 or issue.state == "canceled_no_initiative_admitted"
193 or issue.state == "canceled_by_admin"
194 then
195 negative_event = true
196 end
197 end
199 local class= "event_info"
201 if negative_event then
202 class = class .. " negative"
203 end
205 if not for_issue and not for_initiative then
206 ui.container{ attr = { class = "mdl-card__title mdl-card--has-fab mdl-card--border card-issue" }, content = function()
207 ui.container{ attr = { class = "contextlinks" }, content = function()
208 if not (app.single_unit_id and config.single_area_id) then
209 if not app.single_unit_id then
210 ui.icon("group")
211 slot.put(" ")
212 ui.link{
213 module = "index", view = "index", params = { unit = issue.area.unit_id },
214 attr = { class = "unit" }, content = issue.area.unit.name }
215 end
216 if not config.single_area_id then
217 if not app.single_unit_id then
218 slot.put(" &nbsp;&nbsp;&nbsp; ")
219 end
220 ui.icon("category")
221 slot.put(" ")
222 ui.link{
223 module = "index", view = "index", params = { unit = issue.area.unit_id, area = issue.area_id },
224 attr = { class = "area" }, content = issue.area.name
225 }
226 end
227 end
228 slot.put(" &nbsp;&nbsp;&nbsp; ")
229 ui.icon("gavel")
230 slot.put(" ")
231 ui.link{
232 module = "issue", view = "show", id = issue.id,
233 attr = { class = "issue" }, content = issue.name
234 }
235 -- end }
236 -- ui.container{ attr = { class = "mdl-card__subtitle-text .mdl-cell--hide-phone" }, content = function()
237 ui.container{ attr = { class = class, style = "float: right; color: #fff;" }, content = function ()
238 if event_icon then
239 ui.tag{ tag = "i", attr = { class = "material-icons", ["aria-hidden"] = "true" }, content = event_icon }
240 end
241 slot.put(" ")
242 ui.tag { content = event_name }
243 slot.put(" ")
244 ui.tag{ content = "(" .. days_ago_text .. ")" }
245 end }
246 end }
247 if app.session.member and issue.fully_frozen and not issue.closed and not issue.member_info.direct_voted and app.session.member:has_voting_right_for_unit_id(issue.area.unit_id) then
248 ui.link {
249 attr = { class = "mdl-button mdl-js-button mdl-button--fab mdl-button--colored" ,
250 style = "position: absolute; right: 20px; bottom: -27px;"
251 },
252 module = "vote", view = "list",
253 params = { issue_id = issue.id },
254 content = function()
255 ui.tag{ tag = "i", attr = { class = "material-icons" }, content = config.voting_icon or "mail_outline" }
256 end
257 }
258 end
259 end }
260 end
262 if event.suggestion_id then
263 ui.container{ attr = { class = "suggestion" }, content = function()
264 ui.link{
265 text = format.string(event.suggestion.name, {
266 truncate_at = 160, truncate_suffix = true
267 }),
268 module = "initiative", view = "show", id = event.initiative.id,
269 params = { suggestion_id = event.suggestion_id },
270 anchor = "s" .. event.suggestion_id
271 }
272 end }
273 end
275 if not for_initiative and (not for_issue or event.initiative_id) then
277 ui.container{ attr = { class = "initiative_list" }, content = function()
278 if event.initiative_id then
279 local initiative = event.initiative
281 execute.view{ module = "initiative", view = "_list", params = {
282 issue = issue,
283 initiative = initiative,
284 for_event = mode == "timeline" and not (event.state == issue.state)
286 } }
287 else
288 local initiatives = issue.initiatives
289 execute.view{ module = "initiative", view = "_list", params = {
290 issue = issue,
291 initiatives = initiatives,
292 for_event = mode == "timeline" and not (event.state == issue.state)
293 } }
294 end
295 end }
296 end
297 if
298 app.session.member_id and (
299 (not issue.fully_frozen and app.session.member:has_initiative_right_for_unit_id(issue.area.unit_id))
300 or (issue.fully_frozen and app.session.member:has_voting_right_for_unit_id(issue.area.unit_id))
301 )
302 then
303 ui.container{ attr = { class = "mdl-card__actions mdl-card--border" }, content = function()
304 execute.view{
305 module = "delegation", view = "_info", params = {
306 issue = issue, member = for_member
307 }
308 }
309 end }
310 end
311 end }
313 end
315 if mode == "timeline" then
316 if for_sidebar then
317 ui.container { attr = { class = row_class }, content = function ()
318 ui.link{
319 attr = { class = "moreLink" },
320 text = _"Show full history",
321 module = "initiative", view = "history", id = for_initiative.id
322 }
323 end }
324 elseif #items > limit then
325 ui.container { attr = { class = row_class }, content = function ()
326 local params = request.get_param_strings()
327 ui.link{
328 attr = { class = "moreLink" },
329 text = _"Show older events",
330 module = request.get_module(),
331 view = request.get_view(),
332 id = for_unit and for_unit.id or for_area and for_area.id or for_issue and for_issue.id or for_member and for_member.id,
333 params = {
334 mode = "timeline",
335 event_max_id = last_event_id,
336 tab = params["tab"],
337 phase = params["phase"],
338 closed = params["closed"]
339 }
340 }
341 end }
342 elseif #items < 1 then
343 ui.container { attr = { class = row_class }, content = _"No more events available" }
344 end
345 end
347 if #items < 1 then
348 ui.section( function()
349 ui.sectionRow( function()
350 ui.container{ content = _"No results for this selection" }
351 end )
352 end )
353 end
356 end
359 local filters = {}
361 if not for_initiative and not for_issue and not no_filter then
363 filters = execute.chunk{
364 module = "issue", chunk = "_filters", params = {
365 for_events = mode == "timeline" and true or false,
366 member = app.session.member,
367 for_member = for_member,
368 state = for_state,
369 for_unit = for_unit and true or false,
370 for_area = for_area and true or false
371 }}
372 end
374 filters.opened = true
375 filters.selector = selector
378 local function dotabs()
379 slot.select("filter", function()
380 ui.container{ attr = { class = "mdl-tabs mdl-js-tabs mdl-js-ripple-effect float-left" }, content = function()
381 ui.container{ attr = { class = "mdl-tabs__tab-bar" }, content = function()
382 local mode = request.get_param{ name = "mode" }
383 local css_active = (not mode or mode == "issue") and " is-active" or ""
384 ui.link{ module = request.get_module(), view = request.get_view(), id = request.get_id_string(), content = "Issues", attr = { class = "mdl-tabs__tab" .. css_active } }
385 local css_active = mode and " is-active" or " "
386 ui.link{ module = request.get_module(), view = request.get_view(), id = request.get_id_string(), params = { mode = "timeline" }, content = "Timeline", attr = { class = "mdl-tabs__tab" .. css_active } }
387 ui.link{ module = "member", view = "list", content = "Member", attr = { class = "mdl-tabs__tab" } }
388 end }
389 end }
390 end)
391 end
394 if mode == "timeline" then
395 --dotabs()
396 filters.content = function()
397 doit()
398 end
399 else
400 -- dotabs()
401 filters.content = function()
402 if config.voting_only then
403 local admission_order_field = "filter_issue_order.order_in_unit"
404 if for_area then
405 admission_order_field = "filter_issue_order.order_in_area"
406 end
407 selector:left_join ( "issue_order_in_admission_state", "filter_issue_order", "filter_issue_order.id = issue.id" )
408 selector:add_order_by ( "issue.closed DESC NULLS FIRST" )
409 selector:add_order_by ( "issue.accepted ISNULL" )
410 selector:add_order_by ( "CASE WHEN issue.accepted ISNULL THEN NULL ELSE justify_interval(coalesce(issue.fully_frozen + issue.voting_time, issue.half_frozen + issue.verification_time, issue.accepted + issue.discussion_time, issue.created + issue.max_admission_time) - now()) END" )
411 selector:add_order_by ( "CASE WHEN issue.accepted ISNULL THEN " .. admission_order_field .. " ELSE NULL END" )
412 selector:add_order_by ( "id" )
413 end
414 if not search then
415 -- execute.view{ module = "index", view = "_head" }
416 end
417 ui.paginate{
418 selector = selector,
419 per_page = 25,
420 link_attr = { class = "mdl-button mdl-js-button mdl-button--raised" },
421 active_link_attr = { class = "mdl-button mdl-js-button mdl-button--raised mdl-button--colored" },
422 position = "after",
423 content = doit
424 }
425 end
426 end
428 filters.class = "mdl-special-card mdl-card__fullwidth mdl-shadow--2dp"
430 filters.legend = _"Filter issues:"
432 ui.filters(filters)

Impressum / About Us