Merge branch 'feature/38339-add-mark-notification-button-to-split-screen' of github.com:opf/openproject into feature/38339-add-mark-notification-button-to-split-screen
commit
ffe9c46e52
@ -0,0 +1,36 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
# AssociatedJournals that belong to another journal reflecting |
||||
# an has_many relation (e.g. custom_values) on the journaled object. |
||||
class Journal::AssociatedJournal < ApplicationRecord |
||||
self.abstract_class = true |
||||
|
||||
belongs_to :author, class_name: 'User' |
||||
belongs_to :journal |
||||
end |
@ -0,0 +1,42 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
# Return mail notifications destined at the provided recipient |
||||
module Notifications::Scopes |
||||
module Recipient |
||||
extend ActiveSupport::Concern |
||||
|
||||
class_methods do |
||||
def recipient(user) |
||||
where(recipient_id: user.is_a?(User) ? user.id : user) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,42 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
# Return mail notifications that are unread (have read_mail: false) |
||||
module Notifications::Scopes |
||||
module UnreadMail |
||||
extend ActiveSupport::Concern |
||||
|
||||
class_methods do |
||||
def unread_mail |
||||
where(read_mail: false) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,42 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
# Return digest mail notifications that are unread (have read_digest_mail: false) |
||||
module Notifications::Scopes |
||||
module UnreadMailDigest |
||||
extend ActiveSupport::Concern |
||||
|
||||
class_methods do |
||||
def unread_mail_digest |
||||
where(read_mail_digest: false) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,49 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
# Returns a scope of users watching the instance that should be notified via whatever channel upon updates to the instance. |
||||
# The users need to have the necessary permissions to see the instance as defined by the watchable_permission. |
||||
# Additionally, the users need to have their mail notification setting set to watched: true. |
||||
module Users::Scopes |
||||
module WatcherRecipients |
||||
extend ActiveSupport::Concern |
||||
|
||||
class_methods do |
||||
def watcher_recipients(model) |
||||
model |
||||
.possible_watcher_users |
||||
.where(id: NotificationSetting |
||||
.applicable(model.project) |
||||
.where(watched: true, user_id: model.watcher_users) |
||||
.select(:user_id)) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,311 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
class Notifications::CreateFromModelService |
||||
MENTION_USER_ID_PATTERN = |
||||
'<mention[^>]*(?:data-type="user"[^>]*data-id="(\d+)")|(?:data-id="(\d+)"[^>]*data-type="user")[^>]*>)|(?:\buser#(\d+)\b' |
||||
.freeze |
||||
MENTION_USER_LOGIN_PATTERN = |
||||
'\buser:"(.+?)"'.freeze |
||||
MENTION_GROUP_ID_PATTERN = |
||||
'<mention[^>]*(?:data-type="group"[^>]*data-id="(\d+)")|(?:data-id="(\d+)"[^>]*data-type="group")[^>]*>)|(?:\bgroup#(\d+)\b' |
||||
.freeze |
||||
MENTION_PATTERN = Regexp.new("(?:#{MENTION_USER_ID_PATTERN})|(?:#{MENTION_USER_LOGIN_PATTERN})|(?:#{MENTION_GROUP_ID_PATTERN})") |
||||
|
||||
def initialize(model) |
||||
self.model = model |
||||
end |
||||
|
||||
def call(send_notifications) |
||||
result = ServiceResult.new success: !abort_sending?(send_notifications) |
||||
|
||||
return result if result.failure? |
||||
|
||||
notification_receivers.each do |recipient_id, channel_reasons| |
||||
call = create_notification(recipient_id, channel_reasons) |
||||
result.add_dependent!(call) |
||||
end |
||||
|
||||
result |
||||
end |
||||
|
||||
private |
||||
|
||||
attr_accessor :model |
||||
|
||||
def create_notification(recipient_id, channel_reasons) |
||||
notification_attributes = { |
||||
recipient_id: recipient_id, |
||||
project: project, |
||||
resource: resource, |
||||
journal: journal, |
||||
actor: user_with_fallback |
||||
}.merge(channel_attributes(channel_reasons)) |
||||
|
||||
Notifications::CreateService |
||||
.new(user: user_with_fallback) |
||||
.call(notification_attributes) |
||||
end |
||||
|
||||
def channel_attributes(channel_reasons) |
||||
channel_attributes_mail(channel_reasons) |
||||
.merge(channel_attributes_mail_digest(channel_reasons)) |
||||
.merge(channel_attributes_ian(channel_reasons)) |
||||
end |
||||
|
||||
def channel_attributes_mail(channel_reasons) |
||||
{ |
||||
read_mail: strategy.supports_mail? && channel_reasons.keys.include?('mail') ? false : nil, |
||||
reason_mail: strategy.supports_mail? && channel_reasons['mail']&.first |
||||
} |
||||
end |
||||
|
||||
def channel_attributes_mail_digest(channel_reasons) |
||||
{ |
||||
read_mail_digest: strategy.supports_mail_digest? && channel_reasons.keys.include?('mail_digest') ? false : nil, |
||||
reason_mail_digest: strategy.supports_mail_digest? && channel_reasons['mail_digest']&.first |
||||
} |
||||
end |
||||
|
||||
def channel_attributes_ian(channel_reasons) |
||||
{ |
||||
read_ian: strategy.supports_ian? && channel_reasons.keys.include?('in_app') ? false : nil, |
||||
reason_ian: strategy.supports_ian? && channel_reasons['in_app']&.first |
||||
} |
||||
end |
||||
|
||||
def notification_receivers |
||||
receivers = receivers_hash |
||||
|
||||
strategy.reasons.each do |reason| |
||||
add_receivers_by_reason(receivers, reason) |
||||
end |
||||
|
||||
remove_self_recipient(receivers) |
||||
|
||||
receivers |
||||
end |
||||
|
||||
def add_receivers_by_reason(receivers, reason) |
||||
add_receiver(receivers, send(:"settings_of_#{reason}"), reason) |
||||
end |
||||
|
||||
def settings_of_mentioned |
||||
applicable_settings(mentioned_ids, |
||||
project, |
||||
:mentioned) |
||||
end |
||||
|
||||
def settings_of_involved |
||||
scope = User |
||||
.where(id: group_or_user_ids(journal.data.assigned_to)) |
||||
.or(User.where(id: group_or_user_ids(journal.data.responsible))) |
||||
|
||||
applicable_settings(scope, |
||||
project, |
||||
:involved) |
||||
end |
||||
|
||||
def settings_of_subscribed |
||||
applicable_settings(strategy.subscribed_users(model), |
||||
project, |
||||
:all) |
||||
end |
||||
|
||||
def settings_of_watched |
||||
applicable_settings(strategy.watcher_users(model), |
||||
project, |
||||
:watched) |
||||
end |
||||
|
||||
def settings_of_commented |
||||
return NotificationSetting.none unless journal.notes? |
||||
|
||||
applicable_settings(User.all, |
||||
project, |
||||
:work_package_commented) |
||||
end |
||||
|
||||
def settings_of_created |
||||
return NotificationSetting.none unless journal.initial? |
||||
|
||||
applicable_settings(User.all, |
||||
project, |
||||
:work_package_created) |
||||
end |
||||
|
||||
def settings_of_processed |
||||
return NotificationSetting.none unless !journal.initial? && journal.details.has_key?(:status_id) |
||||
|
||||
applicable_settings(User.all, |
||||
project, |
||||
:work_package_processed) |
||||
end |
||||
|
||||
def settings_of_prioritized |
||||
return NotificationSetting.none unless !journal.initial? && journal.details.has_key?(:priority_id) |
||||
|
||||
applicable_settings(User.all, |
||||
project, |
||||
:work_package_prioritized) |
||||
end |
||||
|
||||
def settings_of_scheduled |
||||
if journal.initial? || !(journal.details.has_key?(:start_date) || journal.details.has_key?(:due_date)) |
||||
return NotificationSetting.none |
||||
end |
||||
|
||||
applicable_settings(User.all, |
||||
project, |
||||
:work_package_scheduled) |
||||
end |
||||
|
||||
def applicable_settings(user_scope, project, reason) |
||||
NotificationSetting |
||||
.applicable(project) |
||||
.where(reason => true) |
||||
.where(user: user_scope.where(id: User.allowed(strategy.permission, project))) |
||||
end |
||||
|
||||
def text_for_mentions |
||||
potential_text = "" |
||||
potential_text << journal.notes if journal.try(:notes) |
||||
|
||||
%i[description subject].each do |field| |
||||
details = journal.details[field] |
||||
|
||||
if details.present? |
||||
potential_text << "\n#{Redmine::Helpers::Diff.new(*details.reverse).additions.join(' ')}" |
||||
end |
||||
end |
||||
potential_text |
||||
end |
||||
|
||||
def mentioned_ids |
||||
matches = mention_matches |
||||
|
||||
base_scope = User |
||||
.includes(:groups) |
||||
.references(:groups_users) |
||||
|
||||
by_id = base_scope.where(id: matches[:user_ids]) |
||||
by_login = base_scope.where(login: matches[:user_login_names]) |
||||
by_group = base_scope.where(groups_users: { id: matches[:group_ids] }) |
||||
|
||||
by_id |
||||
.or(by_login) |
||||
.or(by_group) |
||||
end |
||||
|
||||
def send_notification?(send_notifications) |
||||
send_notifications && ::UserMailer.perform_deliveries |
||||
end |
||||
|
||||
def mention_matches |
||||
text = text_for_mentions |
||||
|
||||
user_ids_tag_after, |
||||
user_ids_tag_before, |
||||
user_ids_hash, |
||||
user_login_names, |
||||
group_ids_tag_after, |
||||
group_ids_tag_before, |
||||
group_ids_hash = text |
||||
.scan(MENTION_PATTERN) |
||||
.transpose |
||||
.each(&:compact!) |
||||
|
||||
{ |
||||
user_ids: [user_ids_tag_after, user_ids_tag_before, user_ids_hash].flatten.compact, |
||||
user_login_names: [user_login_names].flatten.compact, |
||||
group_ids: [group_ids_tag_after, group_ids_tag_before, group_ids_hash].flatten.compact |
||||
} |
||||
end |
||||
|
||||
def abort_sending?(send_notifications) |
||||
!send_notification?(send_notifications) || |
||||
model.nil? || |
||||
!model.class.exists?(id: model.id) || |
||||
journal&.noop? || |
||||
!supported? |
||||
end |
||||
|
||||
def group_or_user_ids(association) |
||||
association.is_a?(Group) ? association.user_ids : association&.id |
||||
end |
||||
|
||||
def user_with_fallback |
||||
user || DeletedUser.first |
||||
end |
||||
|
||||
def add_receiver(receivers, collection, reason) |
||||
collection.each do |notification| |
||||
receivers[notification.user_id][notification.channel] << reason |
||||
end |
||||
end |
||||
|
||||
def remove_self_recipient(receivers) |
||||
receivers.delete(user_with_fallback.id) |
||||
end |
||||
|
||||
def receivers_hash |
||||
Hash.new do |hash, user| |
||||
hash[user] = Hash.new do |channel_hash, channel| |
||||
channel_hash[channel] = [] |
||||
end |
||||
end |
||||
end |
||||
|
||||
def strategy |
||||
@strategy ||= if self.class.const_defined?("#{resource.class}Strategy") |
||||
"#{self.class}::#{resource.class}Strategy".constantize |
||||
end |
||||
end |
||||
|
||||
def supported? |
||||
strategy.present? |
||||
end |
||||
|
||||
def user |
||||
strategy.user(model) |
||||
end |
||||
|
||||
def project |
||||
strategy.project(model) |
||||
end |
||||
|
||||
def resource |
||||
model.is_a?(Journal) ? model.journable : model |
||||
end |
||||
|
||||
def journal |
||||
model.is_a?(Journal) ? model : nil |
||||
end |
||||
end |
@ -0,0 +1,70 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
module Notifications::CreateFromModelService::MessageStrategy |
||||
def self.reasons |
||||
%i(watched subscribed) |
||||
end |
||||
|
||||
def self.permission |
||||
:view_messages |
||||
end |
||||
|
||||
def self.supports_ian? |
||||
false |
||||
end |
||||
|
||||
def self.supports_mail_digest? |
||||
false |
||||
end |
||||
|
||||
def self.supports_mail? |
||||
true |
||||
end |
||||
|
||||
def self.subscribed_users(journal) |
||||
User.notified_on_all(journal.data.project) |
||||
end |
||||
|
||||
def self.watcher_users(journal) |
||||
message = journal.journable |
||||
|
||||
User.watcher_recipients(message.root) |
||||
.or(User.watcher_recipients(message.forum)) |
||||
end |
||||
|
||||
def self.project(journal) |
||||
journal.data.project |
||||
end |
||||
|
||||
def self.user(journal) |
||||
journal.user |
||||
end |
||||
end |
@ -0,0 +1,74 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
module Notifications::CreateFromModelService::WikiContentStrategy |
||||
def self.reasons |
||||
%i(watched subscribed) |
||||
end |
||||
|
||||
def self.permission |
||||
:view_wiki_pages |
||||
end |
||||
|
||||
def self.supports_ian? |
||||
false |
||||
end |
||||
|
||||
def self.supports_mail_digest? |
||||
false |
||||
end |
||||
|
||||
def self.supports_mail? |
||||
true |
||||
end |
||||
|
||||
def self.subscribed_users(journal) |
||||
User.notified_on_all(journal.data.project) |
||||
end |
||||
|
||||
def self.watcher_users(journal) |
||||
page = journal.journable.page |
||||
|
||||
if journal.initial? |
||||
User.watcher_recipients(page.wiki) |
||||
else |
||||
User.watcher_recipients(page.wiki) |
||||
.or(User.watcher_recipients(page)) |
||||
end |
||||
end |
||||
|
||||
def self.project(journal) |
||||
journal.data.project |
||||
end |
||||
|
||||
def self.user(journal) |
||||
journal.user |
||||
end |
||||
end |
@ -0,0 +1,67 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
module Notifications::CreateFromModelService::WorkPackageStrategy |
||||
def self.reasons |
||||
%i(mentioned involved watched subscribed commented created processed prioritized scheduled) |
||||
end |
||||
|
||||
def self.permission |
||||
:view_work_packages |
||||
end |
||||
|
||||
def self.supports_ian? |
||||
true |
||||
end |
||||
|
||||
def self.supports_mail_digest? |
||||
true |
||||
end |
||||
|
||||
def self.supports_mail? |
||||
true |
||||
end |
||||
|
||||
def self.subscribed_users(journal) |
||||
User.notified_on_all(journal.data.project) |
||||
end |
||||
|
||||
def self.watcher_users(journal) |
||||
User.watcher_recipients(journal.journable) |
||||
end |
||||
|
||||
def self.project(journal) |
||||
journal.data.project |
||||
end |
||||
|
||||
def self.user(journal) |
||||
journal.user |
||||
end |
||||
end |
@ -1,120 +0,0 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
class Notifications::JournalWikiMailService |
||||
class << self |
||||
def call(journal, send_mails) |
||||
new(journal) |
||||
.call(send_mails) |
||||
end |
||||
end |
||||
|
||||
attr_reader :journal |
||||
|
||||
def initialize(journal) |
||||
@journal = journal |
||||
end |
||||
|
||||
def call(send_mails) |
||||
return unless send_mail?(send_mails) |
||||
|
||||
if journal.initial? |
||||
send_content_added_mail |
||||
else |
||||
send_content_updated_mail |
||||
end |
||||
end |
||||
|
||||
private |
||||
|
||||
def send_mail?(send_mails) |
||||
send_mails && ::UserMailer.perform_deliveries && !journal.noop? |
||||
end |
||||
|
||||
def send_content_added_mail |
||||
send_content(create_recipients, :wiki_content_added) |
||||
end |
||||
|
||||
def send_content_updated_mail |
||||
send_content(update_recipients, :wiki_content_updated) |
||||
end |
||||
|
||||
def notification_disabled?(name) |
||||
!Setting.notified_events.include?(name) |
||||
end |
||||
|
||||
# Returns the mail addresses of users that should be notified |
||||
def recipients |
||||
project |
||||
.notified_users |
||||
.select { |user| wiki_content.visible?(user) } |
||||
end |
||||
|
||||
def send_content(recipients, method) |
||||
return if notification_disabled?(method.to_s) |
||||
|
||||
recipients.uniq.each do |user| |
||||
UserMailer |
||||
.send(method, user, wiki_content, journal_user) |
||||
.deliver_later |
||||
end |
||||
end |
||||
|
||||
def create_recipients |
||||
recipients + |
||||
wiki.watcher_recipients |
||||
end |
||||
|
||||
def update_recipients |
||||
recipients + |
||||
wiki.watcher_recipients + |
||||
page.watcher_recipients |
||||
end |
||||
|
||||
def wiki_content |
||||
journal.journable |
||||
end |
||||
|
||||
def page |
||||
wiki_content.page |
||||
end |
||||
|
||||
def wiki |
||||
page.wiki |
||||
end |
||||
|
||||
def project |
||||
wiki.project |
||||
end |
||||
|
||||
def journal_user |
||||
journal.user || DeletedUser.first |
||||
end |
||||
end |
@ -1,262 +0,0 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
class Notifications::JournalWpNotificationService |
||||
MENTION_USER_ID_PATTERN = |
||||
'<mention[^>]*(?:data-type="user"[^>]*data-id="(\d+)")|(?:data-id="(\d+)"[^>]*data-type="user")[^>]*>)|(?:\buser#(\d+)\b' |
||||
.freeze |
||||
MENTION_USER_LOGIN_PATTERN = |
||||
'\buser:"(.+?)"'.freeze |
||||
MENTION_GROUP_ID_PATTERN = |
||||
'<mention[^>]*(?:data-type="group"[^>]*data-id="(\d+)")|(?:data-id="(\d+)"[^>]*data-type="group")[^>]*>)|(?:\bgroup#(\d+)\b' |
||||
.freeze |
||||
MENTION_PATTERN = Regexp.new("(?:#{MENTION_USER_ID_PATTERN})|(?:#{MENTION_USER_LOGIN_PATTERN})|(?:#{MENTION_GROUP_ID_PATTERN})") |
||||
|
||||
class << self |
||||
def call(journal, send_notifications) |
||||
return nil if abort_sending?(journal, send_notifications) |
||||
|
||||
notification_receivers(journal).each do |recipient_id, channel_reasons| |
||||
create_notification(journal, |
||||
recipient_id, |
||||
channel_reasons) |
||||
end |
||||
end |
||||
|
||||
private |
||||
|
||||
def create_notification(journal, recipient_id, channel_reasons) |
||||
notification_attributes = { |
||||
recipient_id: recipient_id, |
||||
project: journal.project, |
||||
resource: journal.journable, |
||||
journal: journal, |
||||
actor: user_with_fallback(journal) |
||||
}.merge(channel_attributes(channel_reasons)) |
||||
|
||||
Notifications::CreateService |
||||
.new(user: user_with_fallback(journal)) |
||||
.call(notification_attributes) |
||||
end |
||||
|
||||
def channel_attributes(channel_reasons) |
||||
{ |
||||
read_mail: channel_reasons.keys.include?('mail') ? false : nil, |
||||
read_mail_digest: channel_reasons.keys.include?('mail_digest') ? false : nil, |
||||
read_ian: channel_reasons.keys.include?('in_app') ? false : nil, |
||||
reason_ian: channel_reasons['in_app']&.first, |
||||
reason_mail: channel_reasons['mail']&.first, |
||||
reason_mail_digest: channel_reasons['mail_digest']&.first |
||||
} |
||||
end |
||||
|
||||
def notification_receivers(journal) |
||||
receivers = receivers_hash |
||||
|
||||
%i(mentioned involved watched subscribed commented created processed prioritized scheduled).each do |reason| |
||||
add_receivers_by_reason(receivers, journal, reason) |
||||
end |
||||
|
||||
remove_self_recipient(receivers, journal) |
||||
|
||||
receivers |
||||
end |
||||
|
||||
def add_receivers_by_reason(receivers, journal, reason) |
||||
add_receiver(receivers, send(:"settings_of_#{reason}", journal), reason) |
||||
end |
||||
|
||||
def settings_of_mentioned(journal) |
||||
applicable_settings(mentioned_ids(journal), |
||||
journal.data.project, |
||||
:mentioned) |
||||
end |
||||
|
||||
def settings_of_involved(journal) |
||||
scope = User |
||||
.where(id: group_or_user_ids(journal.data.assigned_to)) |
||||
.or(User.where(id: group_or_user_ids(journal.data.responsible))) |
||||
|
||||
applicable_settings(scope, |
||||
journal.data.project, |
||||
:involved) |
||||
end |
||||
|
||||
def settings_of_subscribed(journal) |
||||
project = journal.data.project |
||||
|
||||
applicable_settings(User.notified_on_all(project), |
||||
project, |
||||
:all) |
||||
end |
||||
|
||||
def settings_of_watched(journal) |
||||
work_package = journal.journable |
||||
|
||||
applicable_settings(work_package.watcher_users, |
||||
work_package.project, |
||||
:watched) |
||||
end |
||||
|
||||
def settings_of_commented(journal) |
||||
return NotificationSetting.none unless journal.notes? |
||||
|
||||
applicable_settings(User.all, |
||||
journal.data.project, |
||||
:work_package_commented) |
||||
end |
||||
|
||||
def settings_of_created(journal) |
||||
return NotificationSetting.none unless journal.initial? |
||||
|
||||
applicable_settings(User.all, |
||||
journal.data.project, |
||||
:work_package_created) |
||||
end |
||||
|
||||
def settings_of_processed(journal) |
||||
return NotificationSetting.none unless !journal.initial? && journal.details.has_key?(:status_id) |
||||
|
||||
applicable_settings(User.all, |
||||
journal.data.project, |
||||
:work_package_processed) |
||||
end |
||||
|
||||
def settings_of_prioritized(journal) |
||||
return NotificationSetting.none unless !journal.initial? && journal.details.has_key?(:priority_id) |
||||
|
||||
applicable_settings(User.all, |
||||
journal.data.project, |
||||
:work_package_prioritized) |
||||
end |
||||
|
||||
def settings_of_scheduled(journal) |
||||
if journal.initial? || !(journal.details.has_key?(:start_date) || journal.details.has_key?(:due_date)) |
||||
return NotificationSetting.none |
||||
end |
||||
|
||||
applicable_settings(User.all, |
||||
journal.data.project, |
||||
:work_package_scheduled) |
||||
end |
||||
|
||||
def applicable_settings(user_scope, project, reason) |
||||
NotificationSetting |
||||
.applicable(project) |
||||
.where(reason => true) |
||||
.where(user: user_scope.where(id: User.allowed(:view_work_packages, project))) |
||||
end |
||||
|
||||
def text_for_mentions(journal) |
||||
potential_text = "" |
||||
potential_text << journal.notes if journal.try(:notes) |
||||
|
||||
%i[description subject].each do |field| |
||||
details = journal.details[field] |
||||
|
||||
if details.present? |
||||
potential_text << "\n#{Redmine::Helpers::Diff.new(*details.reverse).additions.join(' ')}" |
||||
end |
||||
end |
||||
potential_text |
||||
end |
||||
|
||||
def mentioned_ids(journal) |
||||
matches = mention_matches(journal) |
||||
|
||||
base_scope = User |
||||
.includes(:groups) |
||||
.references(:groups_users) |
||||
|
||||
by_id = base_scope.where(id: matches[:user_ids]) |
||||
by_login = base_scope.where(login: matches[:user_login_names]) |
||||
by_group = base_scope.where(groups_users: { id: matches[:group_ids] }) |
||||
|
||||
by_id |
||||
.or(by_login) |
||||
.or(by_group) |
||||
end |
||||
|
||||
def send_notification?(journal, send_notifications) |
||||
send_notifications && ::UserMailer.perform_deliveries |
||||
end |
||||
|
||||
def mention_matches(journal) |
||||
text = text_for_mentions(journal) |
||||
|
||||
user_ids_tag_after, |
||||
user_ids_tag_before, |
||||
user_ids_hash, |
||||
user_login_names, |
||||
group_ids_tag_after, |
||||
group_ids_tag_before, |
||||
group_ids_hash = text |
||||
.scan(MENTION_PATTERN) |
||||
.transpose |
||||
.each(&:compact!) |
||||
|
||||
{ |
||||
user_ids: [user_ids_tag_after, user_ids_tag_before, user_ids_hash].flatten.compact, |
||||
user_login_names: [user_login_names].flatten.compact, |
||||
group_ids: [group_ids_tag_after, group_ids_tag_before, group_ids_hash].flatten.compact |
||||
} |
||||
end |
||||
|
||||
def abort_sending?(journal, send_notifications) |
||||
!send_notification?(journal, send_notifications) || journal.noop? |
||||
end |
||||
|
||||
def group_or_user_ids(association) |
||||
association.is_a?(Group) ? association.user_ids : association&.id |
||||
end |
||||
|
||||
def user_with_fallback(journal) |
||||
journal.user || DeletedUser.first |
||||
end |
||||
|
||||
def add_receiver(receivers, collection, reason) |
||||
collection.each do |notification| |
||||
receivers[notification.user_id][notification.channel] << reason |
||||
end |
||||
end |
||||
|
||||
def remove_self_recipient(receivers, journal) |
||||
receivers.delete(journal.user_id) if receivers[journal.user_id] && !user_with_fallback(journal).pref.self_notified? |
||||
end |
||||
|
||||
def receivers_hash |
||||
Hash.new do |hash, user| |
||||
hash[user] = Hash.new do |channel_hash, channel| |
||||
channel_hash[channel] = [] |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,77 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
class Notifications::MailService |
||||
def initialize(notification) |
||||
self.notification = notification |
||||
end |
||||
|
||||
def call |
||||
ensure_supported |
||||
|
||||
return if ian_read? |
||||
|
||||
strategy.send_mail(notification) |
||||
end |
||||
|
||||
private |
||||
|
||||
attr_accessor :notification |
||||
|
||||
def ensure_supported |
||||
unless supported? |
||||
raise ArgumentError, "Sending mails for notifications is not supported for #{strategy_model}" |
||||
end |
||||
end |
||||
|
||||
def ian_read? |
||||
notification.read_ian |
||||
end |
||||
|
||||
def strategy |
||||
@strategy ||= if self.class.const_defined?("#{strategy_model}Strategy") |
||||
"#{self.class}::#{strategy_model}Strategy".constantize |
||||
end |
||||
end |
||||
|
||||
def strategy_model |
||||
journal&.journable_type || resource&.class |
||||
end |
||||
|
||||
def journal |
||||
notification.journal |
||||
end |
||||
|
||||
def resource |
||||
notification.resource |
||||
end |
||||
|
||||
def supported? |
||||
strategy.present? |
||||
end |
||||
end |
@ -0,0 +1,49 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
module Notifications::MailService::MessageStrategy |
||||
class << self |
||||
def send_mail(notification) |
||||
return if notification_disabled? |
||||
|
||||
UserMailer |
||||
.message_posted( |
||||
notification.recipient, |
||||
notification.resource, |
||||
notification.actor || DeletedUser.first |
||||
) |
||||
.deliver_later |
||||
end |
||||
|
||||
private |
||||
|
||||
def notification_disabled? |
||||
Setting.notified_events.exclude?('message_posted') |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,49 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
module Notifications::MailService::NewsStrategy |
||||
class << self |
||||
def send_mail(notification) |
||||
return if notification_disabled? || !notification.journal.initial? |
||||
|
||||
UserMailer |
||||
.news_added( |
||||
notification.recipient, |
||||
notification.journal.journable, |
||||
notification.journal.user || DeletedUser.first |
||||
) |
||||
.deliver_later |
||||
end |
||||
|
||||
private |
||||
|
||||
def notification_disabled? |
||||
Setting.notified_events.exclude?('news_added') |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,58 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
module Notifications::MailService::WikiContentStrategy |
||||
class << self |
||||
def send_mail(notification) |
||||
method = mailer_method(notification) |
||||
|
||||
return if notification_disabled?(method.to_s) |
||||
|
||||
UserMailer |
||||
.send(method, |
||||
notification.recipient, |
||||
notification.journal.journable, |
||||
notification.journal.user || DeletedUser.first) |
||||
.deliver_later |
||||
end |
||||
|
||||
private |
||||
|
||||
def mailer_method(notification) |
||||
if notification.journal.initial? |
||||
:wiki_content_added |
||||
else |
||||
:wiki_content_updated |
||||
end |
||||
end |
||||
|
||||
def notification_disabled?(name) |
||||
Setting.notified_events.exclude?(name) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,48 @@ |
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
module Notifications::MailService::WorkPackageStrategy |
||||
class << self |
||||
def send_mail(notification) |
||||
journal = notification.journal |
||||
|
||||
UserMailer |
||||
.send(mailer_method(notification), |
||||
notification.recipient, |
||||
journal, |
||||
notification.journal.user || DeletedUser.first) |
||||
.deliver_later |
||||
end |
||||
|
||||
private |
||||
|
||||
def mailer_method(notification) |
||||
notification.journal.initial? ? :work_package_added : :work_package_updated |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,81 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
# A StateMachineJob is a job that consists of multiple steps to complete where a step needs |
||||
# to be finished before the next step is to be taken. Between each step, an amount of time may have to pass. |
||||
# A job including this concern can define step-blocks that will be executed one after another. |
||||
# A step receives the return value of the previous step-block as input. In case a waiting time is specified |
||||
# the job will reschedule itself. |
||||
module StateMachineJob |
||||
extend ActiveSupport::Concern |
||||
|
||||
included do |
||||
def perform(state, *args) |
||||
results = instance_exec(*args, &states[state][:block]) |
||||
|
||||
to = states[state][:to] |
||||
|
||||
switch_state(to, *results) if to |
||||
end |
||||
|
||||
class_attribute :states, |
||||
instance_writer: false, |
||||
default: {} |
||||
|
||||
# Defines a new step, that can then be executed either by starting at this step |
||||
# (by providing the step name as the first parameter of a call to #perform), or as a step |
||||
# in a chain of steps. |
||||
# @param name<Symbol> The name of the step that serves as an identifier to subsequent calls. |
||||
# @param to<Symbol, NilClass> The name of the step triggered after this step. If the value is *nil*, no subsequent |
||||
# step will be triggered. |
||||
# @param wait<Lambda> The result of the lambda dictates the amount of time the job waits before the next step is |
||||
# executed |
||||
# @param block<Proc> The code to execute as part of the step. The block will be executed in the context of the |
||||
# job instance, not the class. |
||||
def self.state(name, to: nil, wait: nil, &block) |
||||
states[name] = { to: to, wait: wait, block: block } |
||||
end |
||||
|
||||
private |
||||
|
||||
def switch_state(to, *results) |
||||
wait = states[to][:wait] |
||||
|
||||
if wait |
||||
self |
||||
.class |
||||
.set(wait: wait.call) |
||||
.perform_later(to, *results) |
||||
else |
||||
perform(to, *results) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,85 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 the OpenProject GmbH |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License version 3. |
||||
# |
||||
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: |
||||
# Copyright (C) 2006-2013 Jean-Philippe Lang |
||||
# Copyright (C) 2010-2013 the ChiliProject Team |
||||
# |
||||
# This program is free software; you can redistribute it and/or |
||||
# modify it under the terms of the GNU General Public License |
||||
# as published by the Free Software Foundation; either version 2 |
||||
# of the License, or (at your option) any later version. |
||||
# |
||||
# This program is distributed in the hope that it will be useful, |
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
# GNU General Public License for more details. |
||||
# |
||||
# You should have received a copy of the GNU General Public License |
||||
# along with this program; if not, write to the Free Software |
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||||
# |
||||
# See docs/COPYRIGHT.rdoc for more details. |
||||
#++ |
||||
|
||||
# Governs the workflow of how journals are passed through: |
||||
# 1) The notifications for any event (e.g. journal creation) is to be created as fast as possible |
||||
# so that it becomes visible as an in app notification. If the resource passed in is indeed a journal, |
||||
# it might get replaced later on (by a subsequent journal). This will lead to notifications being removed. |
||||
# 2) After the journal aggregation time has passed as well as the desired delay, the direct email is sent out. |
||||
# 3) At the same time (TODO: but it could already have been triggered after the aggregation time has passed) |
||||
# the digest is scheduled. |
||||
# This order has to be kept to ensure that the notifications are created before email sending is attempted. If it weren't |
||||
# guaranteed, with the notifications created in one job and the mails send in another, the mail sending job might get executed |
||||
# without any notifications being created which would result in no emails being sent at all. An alternative would be to |
||||
# decouple notification creation and mail sending from another. But then, in app notifications being read could not prevent |
||||
# mails being sent out. |
||||
class Notifications::WorkflowJob < ApplicationJob |
||||
include ::StateMachineJob |
||||
|
||||
queue_with_priority :notification |
||||
|
||||
# In case a resource (e.g. journal) cannot be deserialized (which means fetching it from the db) |
||||
# the resource has been removed which might happen. In that case, no notifications |
||||
# need to be sent out any more. |
||||
discard_on ActiveJob::DeserializationError |
||||
|
||||
state :create_notifications, |
||||
to: :send_mails do |resource, send_notification| |
||||
Notifications::CreateFromModelService |
||||
.new(resource) |
||||
.call(send_notification) |
||||
.all_results |
||||
.map(&:id) |
||||
end |
||||
|
||||
state :send_mails, |
||||
wait: -> { |
||||
Setting.notification_email_delay_minutes.minutes + Setting.journal_aggregation_time_minutes.to_i.minutes |
||||
} do |*notification_ids| |
||||
next unless notification_ids |
||||
|
||||
Notification |
||||
.where(id: notification_ids) |
||||
.unread_mail |
||||
.each do |notification| |
||||
Notifications::MailService |
||||
.new(notification) |
||||
.call |
||||
end |
||||
|
||||
Notification |
||||
.where(id: notification_ids) |
||||
.unread_mail_digest |
||||
.each do |notification| |
||||
Mails::DigestJob |
||||
.schedule(notification) |
||||
end |
||||
end |
||||
end |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue