liquid_feedback_frontend
changeset 1248:c0fd12b97d65
Changes on notifications system, newsletter support added
author | bsw |
---|---|
date | Tue Apr 05 20:40:37 2016 +0200 (2016-04-05) |
parents | 9578cef4018a |
children | 62f7e7d4f9ec |
files | app/main/_prefork/10_init.lua app/main/admin/_action/newsletter_update.lua app/main/admin/index.lua app/main/admin/newsletter_edit.lua app/main/admin/newsletter_list.lua app/main/index/_index_member.lua app/main/index/_notifications.lua app/main/index/_sidebar_notifications.lua app/main/member/_action/update_notify_level.lua app/main/member/settings_notification.lua model/event.lua model/initiative_for_notification.lua model/newsletter.lua model/newsletter_to_send.lua model/scheduled_notification_to_send.lua |
line diff
1.1 --- a/app/main/_prefork/10_init.lua Wed Jan 27 11:16:26 2016 +0100 1.2 +++ b/app/main/_prefork/10_init.lua Tue Apr 05 20:40:37 2016 +0200 1.3 @@ -134,8 +134,20 @@ 1.4 proto = "interval", 1.5 name = "send_pending_notifications", 1.6 delay = 5, 1.7 - handler = function() 1.8 - Event:send_pending_notifications() 1.9 + handler = function() 1.10 + while true do 1.11 + local did_work = false 1.12 + local tmp 1.13 + tmp = Newsletter:send_next_newsletter() 1.14 + if tmp then did_work = true end 1.15 + tmp = Event:send_next_notification() 1.16 + if tmp then did_work = true end 1.17 + tmp = InitiativeForNotification:notify_next_member() 1.18 + if tmp then did_work = true end 1.19 + if not did_work then 1.20 + break 1.21 + end 1.22 + end 1.23 end 1.24 }, 1.25 min_fork = 1,
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 2.2 +++ b/app/main/admin/_action/newsletter_update.lua Tue Apr 05 20:40:37 2016 +0200 2.3 @@ -0,0 +1,29 @@ 2.4 +local id = param.get_id() 2.5 + 2.6 +local newsletter 2.7 + 2.8 +if id then 2.9 + newsletter = Newsletter:by_id(id) 2.10 + if newsletter.sent then 2.11 + slot.select("error", function() 2.12 + ui.tag{ content = _"Newsletter has already been sent out" } 2.13 + end) 2.14 + return false 2.15 + end 2.16 +else 2.17 + newsletter = Newsletter:new() 2.18 +end 2.19 + 2.20 +newsletter.published = param.get("published") 2.21 +if newsletter.published == nil or newsletter.published == "" then 2.22 + newsletter.published = "now" 2.23 +end 2.24 +newsletter.unit_id = param.get("unit_id", atom.integer) 2.25 +if newsletter.unit_id == 0 then 2.26 + newsletter.unit_id = nil 2.27 +end 2.28 +newsletter.include_all_members = param.get("include_all_members", atom.boolean) 2.29 +newsletter.subject = param.get("subject") 2.30 +newsletter.content = param.get("content") 2.31 + 2.32 +newsletter:save()
3.1 --- a/app/main/admin/index.lua Wed Jan 27 11:16:26 2016 +0100 3.2 +++ b/app/main/admin/index.lua Tue Apr 05 20:40:37 2016 +0200 3.3 @@ -38,6 +38,27 @@ 3.4 3.5 ui.sidebar( "tab-whatcanido", function() 3.6 ui.sidebarHead( function() 3.7 + ui.heading { level = 2, content = _"Newsletter" } 3.8 + end ) 3.9 + 3.10 + ui.sidebarSection( "moreLink", function() 3.11 + ui.link{ 3.12 + text = _"Create a newsletter", 3.13 + module = "admin", 3.14 + view = "newsletter_edit" 3.15 + } 3.16 + end ) 3.17 + ui.sidebarSection( "moreLink", function() 3.18 + ui.link{ 3.19 + text = _"Manage newsletters", 3.20 + module = "admin", 3.21 + view = "newsletter_list" 3.22 + } 3.23 + end ) 3.24 +end ) 3.25 + 3.26 +ui.sidebar( "tab-whatcanido", function() 3.27 + ui.sidebarHead( function() 3.28 ui.heading { level = 2, content = _"Cancel issue" } 3.29 end ) 3.30
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/app/main/admin/newsletter_edit.lua Tue Apr 05 20:40:37 2016 +0200 4.3 @@ -0,0 +1,59 @@ 4.4 +local id = param.get_id() 4.5 + 4.6 +local newsletter = {} 4.7 + 4.8 +if id then 4.9 + newsletter = Newsletter:by_id(id) 4.10 +end 4.11 + 4.12 +ui.titleAdmin(_"Newsletter") 4.13 + 4.14 +ui.form{ 4.15 + attr = { class = "vertical section" }, 4.16 + module = "admin", 4.17 + action = "newsletter_update", 4.18 + id = newsletter and newsletter.id, 4.19 + record = newsletter, 4.20 + routing = { 4.21 + default = { 4.22 + mode = "redirect", 4.23 + modules = "admin", 4.24 + view = "newsletter_list" 4.25 + } 4.26 + }, 4.27 + content = function() 4.28 + 4.29 + ui.sectionHead( function() 4.30 + ui.heading { level = 1, content = newsletter and (newsletter.subject) or _"New newsletter" } 4.31 + end ) 4.32 + 4.33 + ui.sectionRow( function() 4.34 + local units = { 4.35 + { id = 0, name = _"All members" }, 4.36 + { id = "_", name = _"" }, 4.37 + } 4.38 + for i, unit in ipairs(Unit:get_flattened_tree()) do 4.39 + units[#units+1] = unit 4.40 + end 4.41 + ui.field.text{ label = _"Date", name = "published" } 4.42 + ui.field.select{ 4.43 + label = "Recipient", 4.44 + name = "unit_id", 4.45 + foreign_records = units, 4.46 + foreign_id = "id", 4.47 + foreign_name = "name", 4.48 + disabled_records = { ["_"] = true }, 4.49 + value = newsletter.unit_id 4.50 + } 4.51 + ui.field.boolean{ label = _"Override disable notifications?", name = "include_all_members" } 4.52 + slot.put("<br />") 4.53 + ui.field.text{ label = _"Subject", name = "subject" } 4.54 + ui.field.text{ label = _"Content", name = "content", multiline = true, attr = { rows = "20" } } 4.55 + 4.56 + ui.submit{ text = _"create newsletter" } 4.57 + slot.put(" ") 4.58 + ui.link { module = "admin", view = "index", content = _"cancel" } 4.59 + end ) 4.60 + end 4.61 +} 4.62 +
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/app/main/admin/newsletter_list.lua Tue Apr 05 20:40:37 2016 +0200 5.3 @@ -0,0 +1,32 @@ 5.4 +local newsletter = Newsletter:new_selector() 5.5 + :add_order_by("published DESC") 5.6 + :exec() 5.7 + 5.8 +ui.titleAdmin(_"Newsletter") 5.9 + 5.10 +ui.section( function() 5.11 + 5.12 + ui.sectionHead( function() 5.13 + ui.heading { level = 1, content = _"Newsletter list" } 5.14 + end ) 5.15 + 5.16 + ui.sectionRow( function () 5.17 + 5.18 + ui.list{ 5.19 + records = newsletter, 5.20 + columns = { 5.21 + { label = _"Unit", content = function(r) ui.tag{ content = r.unit and r.unit.name or _"All members" } end }, 5.22 + { name = "published", label = _"Published" }, 5.23 + { name = "subject", label = _"Subject" }, 5.24 + { label = _"sent", content = function(r) 5.25 + if not r.sent then 5.26 + ui.link{ text = _"Edit", module = "admin", view = "newsletter_edit", id = r.id } 5.27 + else 5.28 + ui.tag{ content = format.timestamp(r.sent) } 5.29 + end 5.30 + end } 5.31 + } 5.32 + } 5.33 + 5.34 + end) 5.35 +end)
6.1 --- a/app/main/index/_index_member.lua Wed Jan 27 11:16:26 2016 +0100 6.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 6.3 @@ -1,66 +0,0 @@ 6.4 - 6.5 -local tabs = { 6.6 - module = "index", 6.7 - view = "index" 6.8 -} 6.9 - 6.10 -tabs[#tabs+1] = { 6.11 - name = "areas", 6.12 - label = _"Home", 6.13 - icon = { static = "icons/16/package.png" }, 6.14 - module = "index", 6.15 - view = "_member_home", 6.16 - params = { member = app.session.member } 6.17 -} 6.18 - 6.19 -tabs[#tabs+1] = { 6.20 - name = "timeline", 6.21 - label = _"Latest events", 6.22 - module = "event", 6.23 - view = "_list", 6.24 - params = { } 6.25 -} 6.26 - 6.27 - 6.28 -tabs[#tabs+1] = { 6.29 - name = "open", 6.30 - label = _"Open issues", 6.31 - module = "issue", 6.32 - view = "_list", 6.33 - params = { 6.34 - for_state = "open", 6.35 - issues_selector = Issue:new_selector() 6.36 - :add_where("issue.closed ISNULL") 6.37 - :add_order_by("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()") 6.38 - } 6.39 -} 6.40 - 6.41 -tabs[#tabs+1] = { 6.42 - name = "closed", 6.43 - label = _"Closed issues", 6.44 - module = "issue", 6.45 - view = "_list", 6.46 - params = { 6.47 - for_state = "closed", 6.48 - issues_selector = Issue:new_selector() 6.49 - :add_where("issue.closed NOTNULL") 6.50 - :add_order_by("issue.closed DESC") 6.51 - 6.52 - } 6.53 -} 6.54 - 6.55 -tabs[#tabs+1] = { 6.56 - name = "members", 6.57 - label = _"Members", 6.58 - module = 'member', 6.59 - view = '_list', 6.60 - params = { members_selector = Member:new_selector():add_where("active") } 6.61 -} 6.62 - 6.63 -if not param.get("tab") then 6.64 - execute.view{ 6.65 - module = "index", view = "_notifications" 6.66 - } 6.67 -end 6.68 - 6.69 -ui.tabs(tabs)
7.1 --- a/app/main/index/_notifications.lua Wed Jan 27 11:16:26 2016 +0100 7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 7.3 @@ -1,103 +0,0 @@ 7.4 -local notification_links = {} 7.5 - 7.6 -if app.session.member.notify_email_unconfirmed then 7.7 - notification_links[#notification_links+1] = { 7.8 - module = "index", view = "email_unconfirmed", 7.9 - text = _"Please confirm your email address" 7.10 - } 7.11 -end 7.12 - 7.13 -if app.session.member.notify_level == nil then 7.14 - notification_links[#notification_links+1] = { 7.15 - module = "member", view = "settings_notification", 7.16 - text = _"Please select your preferred notification level" 7.17 - } 7.18 -end 7.19 - 7.20 -if config.check_delegations_interval_soft then 7.21 - local member = Member:new_selector() 7.22 - :add_where({ "id = ?", app.session.member_id }) 7.23 - :add_field({ "now() > COALESCE(last_delegation_check, activated) + ?::interval", config.check_delegations_interval_soft }, "needs_delegation_check_soft") 7.24 - :single_object_mode() 7.25 - :exec() 7.26 - 7.27 - 7.28 - if member.needs_delegation_check_soft then 7.29 - 7.30 - local delegations = Delegation:delegations_to_check_for_member_id(member.id) 7.31 - 7.32 - if #delegations > 0 then 7.33 - notification_links[#notification_links+1] = { 7.34 - module = "index", view = "check_delegations", 7.35 - text = _"Check your delegations!" 7.36 - } 7.37 - end 7.38 - 7.39 - end 7.40 -end 7.41 - 7.42 -local broken_delegations_count = Delegation:selector_for_broken(app.session.member_id):count() 7.43 - 7.44 -if broken_delegations_count > 0 then 7.45 - notification_links[#notification_links+1] = { 7.46 - module = "index", view = "broken_delegations", 7.47 - text = _("#{count} of your outgoing delegation(s) are broken", { count = broken_delegations_count }) 7.48 - } 7.49 - 7.50 -end 7.51 - 7.52 -local selector = Issue:new_selector() 7.53 - :join("area", nil, "area.id = issue.area_id") 7.54 - :join("privilege", nil, { "privilege.unit_id = area.unit_id AND privilege.member_id = ? AND privilege.voting_right", app.session.member_id }) 7.55 - :left_join("direct_voter", nil, { "direct_voter.issue_id = issue.id AND direct_voter.member_id = ?", app.session.member.id }) 7.56 - :left_join("non_voter", nil, { "non_voter.issue_id = issue.id AND non_voter.member_id = ?", app.session.member.id }) 7.57 - :left_join("interest", nil, { "interest.issue_id = issue.id AND interest.member_id = ?", app.session.member.id }) 7.58 - :add_where{ "direct_voter.member_id ISNULL" } 7.59 - :add_where{ "non_voter.member_id ISNULL" } 7.60 - :add_where{ "interest.member_id NOTNULL" } 7.61 - :add_where{ "issue.fully_frozen NOTNULL" } 7.62 - :add_where{ "issue.closed ISNULL" } 7.63 - :add_order_by{ "issue.fully_frozen + issue.voting_time ASC" } 7.64 - 7.65 -local issues_to_vote_count = selector:count() 7.66 -if issues_to_vote_count > 0 then 7.67 - notification_links[#notification_links+1] = { 7.68 - module = "index", view = "index", 7.69 - params = { 7.70 - tab = "open", filter = "frozen", filter_interest = "issue", filter_delegation = "direct", filter_voting = "not_voted" 7.71 - }, 7.72 - text = _("You have not voted #{count} issue(s) you were interested in", { count = issues_to_vote_count }) 7.73 - } 7.74 -end 7.75 - 7.76 -local initiator_invites_count = Initiator:selector_for_invites(app.session.member_id):count() 7.77 - 7.78 -if initiator_invites_count > 0 then 7.79 - notification_links[#notification_links+1] = { 7.80 - module = "index", view = "initiator_invites", 7.81 - text = _("You are invited to #{count} initiative(s)", { count = initiator_invites_count }) 7.82 - } 7.83 -end 7.84 - 7.85 -updated_drafts_count = Initiative:selector_for_updated_drafts(app.session.member_id):count() 7.86 - 7.87 -if updated_drafts_count > 0 then 7.88 - notification_links[#notification_links+1] = { 7.89 - module = "index", view = "updated_drafts", 7.90 - text = _("New drafts for #{count} initiative(s) you are supporting", { count = updated_drafts_count }) 7.91 - } 7.92 -end 7.93 - 7.94 -if #notification_links > 0 then 7.95 - ui.container{ attr = { class = "notifications" }, content = function() 7.96 - ui.tag{ tag = "ul", attr = { class = "notifications" }, content = function() 7.97 - for i, notification_link in ipairs(notification_links) do 7.98 - ui.tag{ tag = "li", content = function() 7.99 - ui.link(notification_link) 7.100 - end } 7.101 - end 7.102 - end } 7.103 - end } 7.104 -end 7.105 - 7.106 -
8.1 --- a/app/main/index/_sidebar_notifications.lua Wed Jan 27 11:16:26 2016 +0100 8.2 +++ b/app/main/index/_sidebar_notifications.lua Tue Apr 05 20:40:37 2016 +0200 8.3 @@ -7,13 +7,6 @@ 8.4 } 8.5 end 8.6 8.7 -if app.session.member.notify_level == nil then 8.8 - notification_links[#notification_links+1] = { 8.9 - module = "member", view = "settings_notification", 8.10 - text = _"Select a notification level" 8.11 - } 8.12 -end 8.13 - 8.14 if config.check_delegations_interval_soft then 8.15 local member = Member:new_selector() 8.16 :add_where({ "id = ?", app.session.member_id })
9.1 --- a/app/main/member/_action/update_notify_level.lua Wed Jan 27 11:16:26 2016 +0100 9.2 +++ b/app/main/member/_action/update_notify_level.lua Tue Apr 05 20:40:37 2016 +0200 9.3 @@ -1,2 +1,2 @@ 9.4 -app.session.member.notify_level = param.get("notify_level") 9.5 +app.session.member.disable_notifications = param.get("disable_notifications") == "true" and true or false 9.6 app.session.member:save()
10.1 --- a/app/main/member/settings_notification.lua Wed Jan 27 11:16:26 2016 +0100 10.2 +++ b/app/main/member/settings_notification.lua Tue Apr 05 20:40:37 2016 +0200 10.3 @@ -36,8 +36,8 @@ 10.4 tag = "input", 10.5 attr = { 10.6 id = "notify_level_all", 10.7 - type = "radio", name = "notify_level", value = "all", 10.8 - checked = app.session.member.notify_level == 'all' and "checked" or nil 10.9 + type = "radio", name = "disable_notifications", value = "false", 10.10 + checked = not app.session.member.disable_notifications and "checked" or nil 10.11 } 10.12 } 10.13 ui.tag{ 10.14 @@ -52,60 +52,9 @@ 10.15 ui.tag{ 10.16 tag = "input", 10.17 attr = { 10.18 - id = "notify_level_discussion", 10.19 - type = "radio", name = "notify_level", value = "discussion", 10.20 - checked = app.session.member.notify_level == 'discussion' and "checked" or nil 10.21 - } 10.22 - } 10.23 - ui.tag{ 10.24 - tag = "label", attr = { ['for'] = "notify_level_discussion" }, 10.25 - content = _"Only for issues reaching the discussion phase" 10.26 - } 10.27 - end } 10.28 - 10.29 - slot.put("<br />") 10.30 - 10.31 - ui.container{ content = function() 10.32 - ui.tag{ 10.33 - tag = "input", 10.34 - attr = { 10.35 - id = "notify_level_verification", 10.36 - type = "radio", name = "notify_level", value = "verification", 10.37 - checked = app.session.member.notify_level == 'verification' and "checked" or nil 10.38 - } 10.39 - } 10.40 - ui.tag{ 10.41 - tag = "label", attr = { ['for'] = "notify_level_verification" }, 10.42 - content = _"Only for issues reaching the verification phase" 10.43 - } 10.44 - end } 10.45 - 10.46 - slot.put("<br />") 10.47 - 10.48 - ui.container{ content = function() 10.49 - ui.tag{ 10.50 - tag = "input", 10.51 - attr = { 10.52 - id = "notify_level_voting", 10.53 - type = "radio", name = "notify_level", value = "voting", 10.54 - checked = app.session.member.notify_level == 'voting' and "checked" or nil 10.55 - } 10.56 - } 10.57 - ui.tag{ 10.58 - tag = "label", attr = { ['for'] = "notify_level_voting" }, 10.59 - content = _"Only for issues reaching the voting phase" 10.60 - } 10.61 - end } 10.62 - 10.63 - slot.put("<br />") 10.64 - 10.65 - ui.container{ content = function() 10.66 - ui.tag{ 10.67 - tag = "input", 10.68 - attr = { 10.69 id = "notify_level_none", 10.70 - type = "radio", name = "notify_level", value = "none", 10.71 - checked = app.session.member.notify_level == 'none' and "checked" or nil 10.72 + type = "radio", name = "disable_notifications", value = "true", 10.73 + checked = app.session.member.disable_notifications and "checked" or nil 10.74 } 10.75 } 10.76 ui.tag{ 10.77 @@ -116,11 +65,6 @@ 10.78 10.79 slot.put("<br />") 10.80 10.81 - ui.container { content = _"Notifications are only send to you about events in the subject areas you subscribed, the issues you are interested in and the initiatives you are supporting." } 10.82 - 10.83 - 10.84 - slot.put("<br />") 10.85 - 10.86 ui.tag{ 10.87 tag = "input", 10.88 attr = {
11.1 --- a/model/event.lua Wed Jan 27 11:16:26 2016 +0100 11.2 +++ b/model/event.lua Tue Apr 05 20:40:37 2016 +0200 11.3 @@ -51,12 +51,12 @@ 11.4 function Event.object:send_notification() 11.5 11.6 local members_to_notify = Member:new_selector() 11.7 - :join("event_seen_by_member", nil, { "event_seen_by_member.seen_by_member_id = member.id AND event_seen_by_member.notify_level <= member.notify_level AND event_seen_by_member.id = ?", self.id } ) 11.8 - :add_where("member.activated NOTNULL AND member.notify_email NOTNULL") 11.9 + :join("event_for_notification", nil, { "event_for_notification.recipient_id = member.id AND event_for_notification.id = ?", self.id } ) 11.10 + --:add_where("member.activated NOTNULL AND member.notify_email NOTNULL") 11.11 -- SAFETY FIRST, NEVER send notifications for events more then 3 days in past or future 11.12 - :add_where("now() - event_seen_by_member.occurrence BETWEEN '-3 days'::interval AND '3 days'::interval") 11.13 + :add_where("now() - event_for_notification.occurrence BETWEEN '-3 days'::interval AND '3 days'::interval") 11.14 -- do not notify a member about the events caused by the member 11.15 - :add_where("event_seen_by_member.member_id ISNULL OR event_seen_by_member.member_id != member.id") 11.16 + :add_where("event_for_notification.member_id ISNULL OR event_for_notification.member_id != member.id") 11.17 :exec() 11.18 11.19 io.stderr:write("Sending notifications for event " .. self.id .. " to " .. (#members_to_notify) .. " members\n") 11.20 @@ -71,32 +71,22 @@ 11.21 { lang = member.lang or config.default_lang or 'en' }, 11.22 function() 11.23 11.24 - local suggestion 11.25 - if self.suggestion_id then 11.26 - suggestion = Suggestion:by_id(self.suggestion_id) 11.27 - if not suggestion then 11.28 - return 11.29 - end 11.30 - end 11.31 - 11.32 - subject = config.mail_subject_prefix .. " " .. self.event_name 11.33 body = body .. _("[event mail] Unit: #{name}", { name = self.issue.area.unit.name }) .. "\n" 11.34 body = body .. _("[event mail] Area: #{name}", { name = self.issue.area.name }) .. "\n" 11.35 body = body .. _("[event mail] Issue: ##{id}", { id = self.issue_id }) .. "\n\n" 11.36 body = body .. _("[event mail] Policy: #{policy}", { policy = self.issue.policy.name }) .. "\n\n" 11.37 - body = body .. _("[event mail] Event: #{event}", { event = self.event_name }) .. "\n\n" 11.38 body = body .. _("[event mail] Phase: #{phase}", { phase = self.state_name }) .. "\n\n" 11.39 11.40 if self.initiative_id then 11.41 url = request.get_absolute_baseurl() .. "initiative/show/" .. self.initiative_id .. ".html" 11.42 - elseif self.suggestion_id then 11.43 - url = request.get_absolute_baseurl() .. "suggestion/show/" .. self.suggestion_id .. ".html" 11.44 else 11.45 url = request.get_absolute_baseurl() .. "issue/show/" .. self.issue_id .. ".html" 11.46 end 11.47 11.48 body = body .. _("[event mail] URL: #{url}", { url = url }) .. "\n\n" 11.49 11.50 + local leading_initiative 11.51 + 11.52 if self.initiative_id then 11.53 local initiative = Initiative:by_id(self.initiative_id) 11.54 body = body .. _("i#{id}: #{name}", { id = initiative.id, name = initiative.name }) .. "\n\n" 11.55 @@ -110,6 +100,9 @@ 11.56 :limit(3) 11.57 :exec() 11.58 for i, initiative in ipairs(initiatives) do 11.59 + if i == 1 then 11.60 + leading_initiative = initiative 11.61 + end 11.62 body = body .. _("i#{id}: #{name}", { id = initiative.id, name = initiative.name }) .. "\n" 11.63 end 11.64 if initiative_count - 3 > 0 then 11.65 @@ -118,10 +111,14 @@ 11.66 body = body .. "\n" 11.67 end 11.68 11.69 - if suggestion then 11.70 - body = body .. _("#{name}\n\n", { name = suggestion.name }) 11.71 + subject = config.mail_subject_prefix 11.72 + 11.73 + if self.event == "issue_state_changed" then 11.74 + subject = subject .. _("State of #{issue} changed to #{state} / Leading: #{initiative}", { issue = self.issue.name, state = Issue:get_state_name_for_state(self.state), initiative = leading_initiative.display_name }) 11.75 + elseif self.event == "initiative_revoked" then 11.76 + subject = subject .. _("Initiative revoked: #{initiative_name}", { initiative_name = self.initiative.display_name }) 11.77 end 11.78 - 11.79 + 11.80 local success = net.send_mail{ 11.81 envelope_from = config.mail_envelope_from, 11.82 from = config.mail_from, 11.83 @@ -175,23 +172,3 @@ 11.84 end 11.85 11.86 end 11.87 - 11.88 -function Event:send_pending_notifications() 11.89 - while true do 11.90 - if not Event:send_next_notification() then 11.91 - break 11.92 - end 11.93 - end 11.94 -end 11.95 - 11.96 -function Event:send_notifications_loop() 11.97 - 11.98 - while true do 11.99 - local did_work = Event:send_next_notification() 11.100 - if not did_work then 11.101 - print "Sleeping 5 second" 11.102 - os.execute("sleep 5") 11.103 - end 11.104 - end 11.105 - 11.106 -end 11.107 \ No newline at end of file
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 12.2 +++ b/model/initiative_for_notification.lua Tue Apr 05 20:40:37 2016 +0200 12.3 @@ -0,0 +1,178 @@ 12.4 +InitiativeForNotification = mondelefant.new_class() 12.5 +InitiativeForNotification.table = 'initiative_for_notification' 12.6 + 12.7 +InitiativeForNotification:add_reference{ 12.8 + mode = 'm1', 12.9 + to = "Initiative", 12.10 + this_key = 'initiative_id', 12.11 + that_key = 'id', 12.12 + ref = 'initiative', 12.13 +} 12.14 + 12.15 + 12.16 +function InitiativeForNotification:notify_member_id(member_id) 12.17 + 12.18 + db:query("BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ") 12.19 + 12.20 + local member = Member:by_id(member_id) 12.21 + locale.set{ lang = member.lang or config.default_lang } 12.22 + 12.23 + io.stderr:write("Sending digest #" .. member.notification_counter .. " to " .. member.notify_email .. "\n") 12.24 + 12.25 + if not member.notify_email then 12.26 + return 12.27 + end 12.28 + 12.29 + local selector = db:new_selector() 12.30 + :add_field("*") 12.31 + :from({ "get_initiatives_for_notification(?)", member_id }, "initiative_for_notification") 12.32 + :join("initiative", nil, "initiative.id = initiative_for_notification.initiative_id") 12.33 + :join("issue", nil, "issue.id = initiative.issue_id") 12.34 + :join("member", nil, "member.id = initiative_for_notification.recipient_id") 12.35 + :add_order_by("md5(initiative_for_notification.recipient_id || '-' || member.notification_counter || '-' || issue.area_id)") 12.36 + :add_order_by("md5(initiative_for_notification.recipient_id || '-' || member.notification_counter || '-' || issue.id)") 12.37 + selector._class = self 12.38 + 12.39 + local initiatives_for_notification = selector:exec() 12.40 + 12.41 + if #initiatives_for_notification < 1 then 12.42 + return 12.43 + end 12.44 + 12.45 + local initiatives = initiatives_for_notification:load("initiative") 12.46 + local issues = initiatives:load("issue") 12.47 + issues:load("area") 12.48 + issues:load("policy") 12.49 + 12.50 + local last_area_id 12.51 + local last_issue_id 12.52 + 12.53 + local draft_count = 0 12.54 + local suggestion_count = 0 12.55 + 12.56 + local ms = {} 12.57 + 12.58 + for i, entry in ipairs(initiatives_for_notification) do 12.59 + local initiative = entry.initiative 12.60 + local issue = initiative.issue 12.61 + local area = issue.area 12.62 + local policy = issue.policy 12.63 + 12.64 + local m = {} 12.65 + if last_area_id ~= area.id then 12.66 + m[#m+1] = "*** " .. area.name .. " ***" 12.67 + m[#m+1] = "" 12.68 + last_area_id = area.id 12.69 + end 12.70 + if last_issue_id ~= issue.id then 12.71 + local state_time_text 12.72 + if string.sub(issue.state_time_left, 1, 1) == "-" then 12.73 + state_time_text = _"Phase ends soon" 12.74 + else 12.75 + state_time_text = _( "#{interval} left", { 12.76 + interval = format.interval_text(issue.state_time_left) 12.77 + }) 12.78 + end 12.79 + m[#m+1] = "---" 12.80 + m[#m+1] = policy.name .. " #" .. issue.id .. " - " .. issue.state_name .. " - " .. state_time_text 12.81 + m[#m+1] = "" 12.82 + last_issue_id = issue.id 12.83 + end 12.84 + m[#m+1] = initiative.display_name 12.85 + local source 12.86 + if entry.supported then 12.87 + source = _"has my support" 12.88 + local draft_info 12.89 + if entry.new_draft then 12.90 + draft_info = _"draft updated" 12.91 + draft_count = draft_count + 1 12.92 + end 12.93 + if draft_info then 12.94 + source = source .. ", " .. draft_info 12.95 + end 12.96 + local info 12.97 + if entry.new_suggestion_count then 12.98 + if entry.new_suggestion_count == 1 then 12.99 + info = _"new suggestion added" 12.100 + elseif entry.new_suggestion_count > 1 then 12.101 + info = _("#{count} suggestions added", { count = entry.new_suggestion_count }) 12.102 + end 12.103 + suggestion_count = suggestion_count + entry.new_suggestion_count 12.104 + end 12.105 + if info then 12.106 + source = source .. ", " .. info 12.107 + end 12.108 + elseif entry.featured then 12.109 + source = _"featured" 12.110 + end 12.111 + if entry.leading then 12.112 + source = source and source .. ", " or "" 12.113 + source = source .. "currently leading" 12.114 + end 12.115 + m[#m+1] = "(" .. source .. ")" 12.116 + m[#m+1] = "" 12.117 + if not initiatives_for_notification[i+1] or initiatives_for_notification[i+1].issue_id ~= issue.id then 12.118 + m[#m+1] = _("more: #{url}", { url = request.get_absolute_baseurl() .. "issue/show/" .. issue.id .. ".html" }) 12.119 + m[#m+1] = "" 12.120 + end 12.121 + ms[#ms+1] = table.concat(m, "\n") 12.122 + end 12.123 + 12.124 + local info = {} 12.125 + 12.126 + if draft_count > 0 then 12.127 + if draft_count == 1 then 12.128 + info[#info+1] = _"draft updated for " .. initiatives_for_notification[1].initiative.display_name 12.129 + else 12.130 + info[#info+1] = _("drafts of #{draft_count} initiatives updated", { draft_count = draft_count }) 12.131 + end 12.132 + end 12.133 + 12.134 + if suggestion_count > 0 then 12.135 + if suggestion_count == 1 then 12.136 + info[#info+1] = _"new suggestion for " .. initiatives_for_notification[1].initiative.display_name 12.137 + else 12.138 + info[#info+1] = _("#{suggestion_count} suggestions added", { suggestion_count = suggestion_count }) 12.139 + end 12.140 + end 12.141 + 12.142 + if draft_count == 0 and suggestion_count == 0 then 12.143 + info[#info+1] = _"featured initiatives" 12.144 + end 12.145 + 12.146 + local subject = _("Digest #{id}: #{info}", { 12.147 + id = member.notification_counter, info = table.concat(info, ", ") 12.148 + }) 12.149 + 12.150 + 12.151 + local template = config.notification_digest_template 12.152 + 12.153 + local message = _(template, { 12.154 + name = member.name, 12.155 + digest = table.concat(ms, "\n") 12.156 + }) 12.157 + 12.158 + local success = net.send_mail{ 12.159 + envelope_from = config.mail_envelope_from, 12.160 + from = config.mail_from, 12.161 + reply_to = config.mail_reply_to, 12.162 + to = member.notify_email, 12.163 + subject = subject, 12.164 + content_type = "text/plain; charset=UTF-8", 12.165 + content = message 12.166 + } 12.167 + 12.168 + db:query("COMMIT") 12.169 + 12.170 +end 12.171 + 12.172 +function InitiativeForNotification:notify_next_member() 12.173 + local scheduled_notification_to_send = ScheduledNotificationToSend:get_next() 12.174 + if not scheduled_notification_to_send then 12.175 + return false 12.176 + end 12.177 + InitiativeForNotification:notify_member_id(scheduled_notification_to_send.recipient_id) 12.178 + return true 12.179 +end 12.180 + 12.181 +ScheduledNotificationToSend:get_next() 12.182 \ No newline at end of file
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 13.2 +++ b/model/newsletter.lua Tue Apr 05 20:40:37 2016 +0200 13.3 @@ -0,0 +1,41 @@ 13.4 +Newsletter = mondelefant.new_class() 13.5 +Newsletter.table = 'newsletter' 13.6 + 13.7 +function Newsletter:send_next_newsletter() 13.8 + local newsletter_to_send = NewsletterToSend:get_next() 13.9 + if not newsletter_to_send then 13.10 + return false 13.11 + end 13.12 + local newsletter = newsletter_to_send.newsletter 13.13 + 13.14 + local newsletter_to_send = NewsletterToSend:by_newsletter_id(newsletter.id) 13.15 + newsletter_to_send:load("member") 13.16 + 13.17 + newsletter.sent = "now" 13.18 + newsletter:save() 13.19 + 13.20 + io.stderr:write("Sending newsletter " .. newsletter.id .. " to " .. (#newsletter_to_send) .. " members\n") 13.21 + 13.22 + for i, n in ipairs(newsletter_to_send) do 13.23 + 13.24 + local member = newsletter_to_send.member 13.25 + 13.26 + if not member.notify_email then 13.27 + return 13.28 + end 13.29 + 13.30 + io.stderr:write("Sending newsletter " .. newsletter.id .. " to " .. member.notify_email .. "\n") 13.31 + 13.32 + local success = net.send_mail{ 13.33 + envelope_from = config.mail_envelope_from, 13.34 + from = config.mail_from, 13.35 + reply_to = config.mail_reply_to, 13.36 + to = member.notify_email, 13.37 + subject = newsletter.subject, 13.38 + content_type = "text/plain; charset=UTF-8", 13.39 + content = newsletter.content 13.40 + } 13.41 + 13.42 + end 13.43 + 13.44 +end 13.45 \ No newline at end of file
14.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 14.2 +++ b/model/newsletter_to_send.lua Tue Apr 05 20:40:37 2016 +0200 14.3 @@ -0,0 +1,34 @@ 14.4 +NewsletterToSend = mondelefant.new_class() 14.5 +NewsletterToSend.table = 'newsletter_to_send' 14.6 + 14.7 +NewsletterToSend:add_reference{ 14.8 + mode = 'm1', 14.9 + to = "Newsletter", 14.10 + this_key = 'newsletter_id', 14.11 + that_key = 'id', 14.12 + ref = 'newsletter', 14.13 +} 14.14 + 14.15 +NewsletterToSend:add_reference{ 14.16 + mode = 'm1', 14.17 + to = "Member", 14.18 + this_key = 'recipient_id', 14.19 + that_key = 'id', 14.20 + ref = 'member', 14.21 +} 14.22 + 14.23 +function NewsletterToSend:get_next() 14.24 + return NewsletterToSend:new_selector() 14.25 + :set_distinct("newsletter_id") 14.26 + :add_order_by("newsletter_id") 14.27 + :limit(1) 14.28 + :optional_object_mode() 14.29 + :exec() 14.30 +end 14.31 + 14.32 +function NewsletterToSend:by_newsletter_id(id) 14.33 + return NewsletterToSend:new_selector() 14.34 + :add_where{ "newsletter_id = ?", id } 14.35 + :optional_object_mode() 14.36 + :exec() 14.37 +end 14.38 \ No newline at end of file
15.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 15.2 +++ b/model/scheduled_notification_to_send.lua Tue Apr 05 20:40:37 2016 +0200 15.3 @@ -0,0 +1,10 @@ 15.4 +ScheduledNotificationToSend = mondelefant.new_class() 15.5 +ScheduledNotificationToSend.table = 'scheduled_notification_to_send' 15.6 + 15.7 +function ScheduledNotificationToSend:get_next() 15.8 + return self:new_selector() 15.9 + :add_order_by("pending DESC") 15.10 + :limit(1) 15.11 + :optional_object_mode() 15.12 + :exec() 15.13 +end 15.14 \ No newline at end of file