commit
c0555a1b38
@ -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) if !user_with_fallback.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 |
||||
|
||||
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 |
@ -0,0 +1,63 @@ |
||||
#-- 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::DocumentStrategy |
||||
def self.reasons |
||||
%i(subscribed) |
||||
end |
||||
|
||||
def self.permission |
||||
:view_documents |
||||
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(project(journal)) |
||||
end |
||||
|
||||
def self.project(journal) |
||||
journal.data.project |
||||
end |
||||
|
||||
def self.user(journal) |
||||
journal.user |
||||
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::DocumentStrategy |
||||
class << self |
||||
def send_mail(notification) |
||||
return if notification_disabled? || !notification.journal.initial? |
||||
|
||||
DocumentsMailer |
||||
.document_added( |
||||
notification.recipient, |
||||
notification.resource |
||||
) |
||||
.deliver_later |
||||
end |
||||
|
||||
private |
||||
|
||||
def notification_disabled? |
||||
Setting.notified_events.exclude?('document_added') |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,162 @@ |
||||
#-- 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. |
||||
#++ |
||||
require 'spec_helper' |
||||
require Rails.root.join('spec/services/notifications/create_from_journal_job_shared') |
||||
|
||||
describe Notifications::CreateFromModelService, 'document', with_settings: { journal_aggregation_time_minutes: 0 } do |
||||
subject(:call) do |
||||
described_class.new(journal).call(send_notifications) |
||||
end |
||||
|
||||
include_context 'with CreateFromJournalJob context' |
||||
|
||||
shared_let(:project) { FactoryBot.create(:project) } |
||||
|
||||
let(:permissions) { [:view_documents] } |
||||
let(:send_notifications) { true } |
||||
|
||||
let(:resource) do |
||||
FactoryBot.create(:document, |
||||
project: project) |
||||
end |
||||
let(:journal) { resource.journals.last } |
||||
let(:author) { other_user } |
||||
|
||||
current_user { other_user } |
||||
|
||||
before do |
||||
recipient |
||||
end |
||||
|
||||
describe '#perform' do |
||||
context 'with a newly created document' do |
||||
context 'with the user having registered for all notifications' do |
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :subscribed, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user having registered for involved notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(involved: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for no notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications but lacking permissions' do |
||||
before do |
||||
recipient.members.destroy_all |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
end |
||||
|
||||
context 'with an updated document' do |
||||
before do |
||||
resource.title = 'A new subject' |
||||
resource.save! |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications' do |
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :subscribed, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user having registered for involved notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(involved: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for no notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications but lacking permissions' do |
||||
before do |
||||
recipient.members.destroy_all |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,102 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe 'News creation and commenting', type: :feature, js: true do |
||||
let(:project) { FactoryBot.create(:project) } |
||||
let!(:other_user) do |
||||
FactoryBot.create(:user, |
||||
member_in_project: project, |
||||
member_with_permissions: %i[]) |
||||
end |
||||
|
||||
current_user do |
||||
FactoryBot.create(:user, |
||||
member_in_project: project, |
||||
member_with_permissions: %i[manage_news comment_news]) |
||||
end |
||||
|
||||
it 'allows creating new and commenting it all of which will result in notifications and mails' do |
||||
visit project_news_index_path(project) |
||||
|
||||
within '.toolbar-items' do |
||||
click_link 'News' |
||||
end |
||||
|
||||
# Create the news |
||||
fill_in 'Title', with: 'My new news' |
||||
fill_in 'Summary', with: 'The news summary' |
||||
|
||||
perform_enqueued_jobs do |
||||
click_button 'Create' |
||||
end |
||||
|
||||
# The new news is visible on the index page |
||||
expect(page) |
||||
.to have_link('My new news') |
||||
|
||||
expect(page) |
||||
.to have_content 'The news summary' |
||||
|
||||
# Creating the news will have sent out mails |
||||
expect(ActionMailer::Base.deliveries.size) |
||||
.to be 1 |
||||
|
||||
expect(ActionMailer::Base.deliveries.last.to) |
||||
.to match_array [other_user.mail] |
||||
|
||||
expect(ActionMailer::Base.deliveries.last.subject) |
||||
.to include 'My new news' |
||||
|
||||
click_link 'My new news' |
||||
|
||||
comment_editor = ::Components::WysiwygEditor.new |
||||
comment_editor.set_markdown "A new **text**" |
||||
|
||||
perform_enqueued_jobs do |
||||
click_button 'Add comment' |
||||
end |
||||
|
||||
# The new comment is visible on the show page |
||||
expect(page) |
||||
.to have_content "A new text" |
||||
|
||||
# Creating the news comment will have sent out mails |
||||
expect(ActionMailer::Base.deliveries.size) |
||||
.to be 2 |
||||
|
||||
expect(ActionMailer::Base.deliveries.last.to) |
||||
.to match_array [other_user.mail] |
||||
|
||||
expect(ActionMailer::Base.deliveries.last.subject) |
||||
.to include 'My new news' |
||||
end |
||||
end |
@ -1,129 +0,0 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe 'acts_as_watchable including model (e.g. WikiPage)', type: :model do |
||||
let(:klass) { WikiPage } |
||||
let(:project) { wiki.project } |
||||
let(:wiki) { FactoryBot.create(:wiki) } |
||||
let(:instance) { FactoryBot.create(:wiki_page, wiki: wiki) } |
||||
|
||||
describe '#watcher_recipients' do |
||||
subject(:watcher_recipients) do |
||||
instance.watcher_recipients |
||||
end |
||||
|
||||
let(:watcher_all_notifications) do |
||||
FactoryBot.create(:watcher, |
||||
watchable: instance, |
||||
user: watcher_user_all_notifications) |
||||
end |
||||
let(:watcher_user_all_notifications) do |
||||
FactoryBot.create(:user, |
||||
member_in_project: project, |
||||
member_with_permissions: %i(view_wiki_pages)) |
||||
end |
||||
|
||||
let(:watcher_watched_notifications) do |
||||
FactoryBot.create(:watcher, |
||||
watchable: instance, |
||||
user: watcher_user_watched_notifications) |
||||
end |
||||
let(:watcher_user_watched_notifications) do |
||||
FactoryBot.create(:user, |
||||
member_in_project: project, |
||||
member_with_permissions: %i(view_wiki_pages)).tap do |user| |
||||
user.notification_settings.mail.update_all(involved: false, |
||||
mentioned: false, |
||||
watched: true, |
||||
all: false) |
||||
end |
||||
end |
||||
|
||||
let(:watcher_no_notifications) do |
||||
FactoryBot.create(:watcher, |
||||
watchable: instance, |
||||
user: watcher_user_no_notifications) |
||||
end |
||||
let(:watcher_user_no_notifications) do |
||||
FactoryBot.create(:user, |
||||
member_in_project: project, |
||||
member_with_permissions: %i(view_wiki_pages)).tap do |user| |
||||
user.notification_settings.mail.update_all(involved: false, |
||||
mentioned: false, |
||||
watched: false, |
||||
all: false) |
||||
end |
||||
end |
||||
|
||||
let(:watcher_no_permission) do |
||||
FactoryBot.create(:watcher, |
||||
:skip_validate, |
||||
watchable: instance, |
||||
user: watcher_user_no_permission) |
||||
end |
||||
let(:watcher_user_no_permission) do |
||||
FactoryBot.create(:user, |
||||
member_in_project: project, |
||||
member_with_permissions: %i()) |
||||
end |
||||
|
||||
let(:watcher_locked) do |
||||
FactoryBot.create(:watcher, |
||||
:skip_validate, |
||||
watchable: instance, |
||||
user: watcher_user_no_permission) |
||||
end |
||||
let(:watcher_user_locked) do |
||||
FactoryBot.create(:locked_user, |
||||
member_in_project: project, |
||||
member_with_permissions: %i(view_wiki_pages)) |
||||
end |
||||
|
||||
let(:non_watcher_user_all_notifications) do |
||||
FactoryBot.create(:user, |
||||
member_in_project: project, |
||||
member_with_permissions: %i(view_wiki_pages)) |
||||
end |
||||
|
||||
before do |
||||
watcher_all_notifications |
||||
watcher_watched_notifications |
||||
watcher_no_notifications |
||||
watcher_no_permission |
||||
|
||||
non_watcher_user_all_notifications |
||||
end |
||||
|
||||
it 'includes users watching the instance and having notification settings and permissions' do |
||||
expect(watcher_recipients) |
||||
.to match_array([watcher_user_all_notifications, watcher_user_watched_notifications]) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,50 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe Notifications::Scopes::UnreadMailDigest, type: :model do |
||||
describe '.unread_digest_mail' do |
||||
subject(:scope) { ::Notification.unread_mail_digest } |
||||
|
||||
let(:no_mail_notification) { FactoryBot.create(:notification, read_mail_digest: nil) } |
||||
let(:unread_mail_notification) { FactoryBot.create(:notification, read_mail_digest: false) } |
||||
let(:read_mail_notification) { FactoryBot.create(:notification, read_mail_digest: true) } |
||||
|
||||
before do |
||||
no_mail_notification |
||||
unread_mail_notification |
||||
read_mail_notification |
||||
end |
||||
|
||||
it 'contains the notifications with read_mail: false' do |
||||
expect(scope) |
||||
.to match_array([unread_mail_notification]) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,50 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe Notifications::Scopes::UnreadMail, type: :model do |
||||
describe '.unread_mail' do |
||||
subject(:scope) { ::Notification.unread_mail } |
||||
|
||||
let(:no_mail_notification) { FactoryBot.create(:notification, read_mail: nil) } |
||||
let(:unread_mail_notification) { FactoryBot.create(:notification, read_mail: false) } |
||||
let(:read_mail_notification) { FactoryBot.create(:notification, read_mail: true) } |
||||
|
||||
before do |
||||
no_mail_notification |
||||
unread_mail_notification |
||||
read_mail_notification |
||||
end |
||||
|
||||
it 'contains the notifications with read_mail: false' do |
||||
expect(scope) |
||||
.to match_array([unread_mail_notification]) |
||||
end |
||||
end |
||||
end |
@ -1,107 +0,0 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe WorkPackage, type: :model do |
||||
describe ActionMailer::Base do |
||||
let(:user_1) do |
||||
FactoryBot.create(:user, |
||||
mail: 'dlopper@somenet.foo', |
||||
member_in_project: project) |
||||
end |
||||
let(:user_2) do |
||||
FactoryBot.create(:user, |
||||
mail: 'jsmith@somenet.foo', |
||||
member_in_project: project) |
||||
end |
||||
let(:project) { FactoryBot.create(:project) } |
||||
let(:work_package) { FactoryBot.build(:work_package, project: project) } |
||||
|
||||
before do |
||||
allow(work_package).to receive(:recipients).and_return([user_1]) |
||||
allow(work_package).to receive(:watcher_recipients).and_return([user_2]) |
||||
|
||||
Journal::NotificationConfiguration.with true do |
||||
perform_enqueued_jobs do |
||||
work_package.save |
||||
end |
||||
end |
||||
end |
||||
|
||||
subject { ActionMailer::Base.deliveries.size } |
||||
|
||||
it do |
||||
expect(subject).to eq 2 |
||||
end |
||||
|
||||
context 'stale object' do |
||||
before do |
||||
wp = WorkPackage.find(work_package.id) |
||||
|
||||
wp.subject = 'Subject update' |
||||
wp.save! |
||||
|
||||
ActionMailer::Base.deliveries.clear |
||||
|
||||
work_package.subject = 'A different subject update' |
||||
begin |
||||
work_package.save! |
||||
rescue StandardError |
||||
nil |
||||
end |
||||
end |
||||
|
||||
it { is_expected.to eq(0) } |
||||
end |
||||
|
||||
context 'no notification' do |
||||
before do |
||||
ActionMailer::Base.deliveries.clear # clear mails sent due to prior WP creation |
||||
|
||||
Journal::NotificationConfiguration.with false do |
||||
work_package.save! |
||||
end |
||||
end |
||||
|
||||
it { is_expected.to eq(0) } |
||||
end |
||||
|
||||
context 'group_assigned_work_package' do |
||||
let(:group) { FactoryBot.create(:group, members: user_1) } |
||||
|
||||
before do |
||||
work_package.assigned_to = group |
||||
end |
||||
|
||||
subject { work_package.recipients } |
||||
|
||||
it { is_expected.to include(user_1) } |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,129 @@ |
||||
#-- 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. |
||||
#++ |
||||
require 'spec_helper' |
||||
|
||||
shared_context 'with CreateFromJournalJob context' do |
||||
shared_let(:project) { FactoryBot.create(:project_with_types) } |
||||
let(:permissions) { [] } |
||||
let(:recipient) do |
||||
FactoryBot.create(:user, |
||||
notification_settings: recipient_notification_settings, |
||||
member_in_project: project, |
||||
member_through_role: FactoryBot.create(:role, permissions: permissions), |
||||
login: recipient_login, |
||||
preferences: { |
||||
no_self_notified: recipient_no_self_notified |
||||
}) |
||||
end |
||||
let(:recipient_login) { "johndoe" } |
||||
let(:recipient_no_self_notified) { true } |
||||
let(:other_user) do |
||||
notification_settings = [ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
|
||||
FactoryBot.create(:user, |
||||
notification_settings: notification_settings) |
||||
end |
||||
let(:notification_settings_all_false) do |
||||
{ |
||||
all: false, |
||||
involved: false, |
||||
watched: false, |
||||
mentioned: false, |
||||
work_package_commented: false, |
||||
work_package_processed: false, |
||||
work_package_created: false, |
||||
work_package_scheduled: false, |
||||
work_package_prioritized: false |
||||
} |
||||
end |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, all: true), |
||||
FactoryBot.build(:in_app_notification_setting, all: true), |
||||
FactoryBot.build(:mail_digest_notification_setting, all: true) |
||||
] |
||||
end |
||||
let(:send_notifications) { true } |
||||
|
||||
shared_examples_for 'creates notification' do |
||||
let(:sender) { author } |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: false, |
||||
reason_ian: :mentioned, |
||||
read_mail: false, |
||||
reason_mail: :mentioned, |
||||
read_mail_digest: false, |
||||
reason_mail_digest: :mentioned |
||||
} |
||||
end |
||||
let(:notification) { FactoryBot.build_stubbed(:notification) } |
||||
|
||||
it 'creates a notification and returns it' do |
||||
notifications_service = instance_double(Notifications::CreateService) |
||||
|
||||
allow(Notifications::CreateService) |
||||
.to receive(:new) |
||||
.with(user: sender) |
||||
.and_return(notifications_service) |
||||
allow(notifications_service) |
||||
.to receive(:call) |
||||
.and_return(ServiceResult.new(success: true, result: notification)) |
||||
|
||||
expect(call.all_results) |
||||
.to match_array([notification]) |
||||
|
||||
expect(notifications_service) |
||||
.to have_received(:call) |
||||
.with({ recipient_id: recipient.id, |
||||
project: project, |
||||
actor: sender, |
||||
journal: journal, |
||||
resource: resource }.merge(notification_channel_reasons)) |
||||
end |
||||
end |
||||
|
||||
shared_examples_for 'creates no notification' do |
||||
it 'creates no notification' do |
||||
allow(Notifications::CreateService) |
||||
.to receive(:new) |
||||
.and_call_original |
||||
|
||||
call |
||||
|
||||
expect(Notifications::CreateService) |
||||
.not_to have_received(:new) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,168 @@ |
||||
#-- 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. |
||||
#++ |
||||
require 'spec_helper' |
||||
require_relative './create_from_journal_job_shared' |
||||
|
||||
describe Notifications::CreateFromModelService, 'comment', with_settings: { journal_aggregation_time_minutes: 0 } do |
||||
subject(:call) do |
||||
described_class.new(resource).call(send_notifications) |
||||
end |
||||
|
||||
include_context 'with CreateFromJournalJob context' |
||||
|
||||
shared_let(:project) { FactoryBot.create(:project) } |
||||
shared_let(:news) { FactoryBot.create(:news, project: project) } |
||||
|
||||
let(:permissions) { [] } |
||||
let(:send_notifications) { true } |
||||
|
||||
let(:resource) do |
||||
FactoryBot.create(:comment, |
||||
commented: news, |
||||
author: author, |
||||
comments: 'Some text') |
||||
end |
||||
|
||||
let(:journal) { nil } |
||||
|
||||
let(:author) { other_user } |
||||
|
||||
current_user { other_user } |
||||
|
||||
before do |
||||
recipient |
||||
end |
||||
|
||||
describe '#perform' do |
||||
context 'with a newly created comment' do |
||||
context 'with the user having registered for all notifications' do |
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :subscribed, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user having registered for involved notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(involved: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for no notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and watching the news' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
news.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :watched, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user not having registered for watcher notifications and watching the news' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
news.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and not watching the news' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications but lacking permissions' do |
||||
before do |
||||
recipient.members.destroy_all |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,364 @@ |
||||
#-- 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. |
||||
#++ |
||||
require 'spec_helper' |
||||
require_relative './create_from_journal_job_shared' |
||||
|
||||
describe Notifications::CreateFromModelService, 'message', with_settings: { journal_aggregation_time_minutes: 0 } do |
||||
subject(:call) do |
||||
described_class.new(journal).call(send_notifications) |
||||
end |
||||
|
||||
include_context 'with CreateFromJournalJob context' |
||||
|
||||
shared_let(:project) { FactoryBot.create(:project) } |
||||
shared_let(:forum) { FactoryBot.create(:forum, project: project) } |
||||
|
||||
let(:permissions) { [:view_messages] } |
||||
let(:send_notifications) { true } |
||||
|
||||
let(:resource) do |
||||
FactoryBot.create(:message, |
||||
forum: forum, |
||||
parent: root_message) |
||||
end |
||||
let(:journal) { resource.journals.last } |
||||
let(:author) { other_user } |
||||
let(:root_message) do |
||||
FactoryBot.create(:message, |
||||
forum: forum) |
||||
end |
||||
|
||||
current_user { other_user } |
||||
|
||||
before do |
||||
recipient |
||||
end |
||||
|
||||
describe '#perform' do |
||||
context 'with a newly created message' do |
||||
context 'with the user having registered for all notifications' do |
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :subscribed, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user having registered for involved notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(involved: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for no notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and watching the forum' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
forum.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :watched, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user not having registered for watcher notifications and watching the forum' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
forum.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and not watching the forum nor root message' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and watching the root' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
root_message.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :watched, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user not having registered for watcher notifications and watching the root' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
root_message.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications but lacking permissions' do |
||||
before do |
||||
recipient.members.destroy_all |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
end |
||||
|
||||
context 'with an updated message' do |
||||
before do |
||||
resource.subject = 'A new subject' |
||||
resource.save! |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications' do |
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :subscribed, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user having registered for involved notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(involved: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for no notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and watching the forum' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
forum.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :watched, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user not having registered for watcher notifications and watching the forum' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
forum.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and not watching the forum nor root message' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and watching the root' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
root_message.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :watched, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user not having registered for watcher notifications and watching the root' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
root_message.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications but lacking permissions' do |
||||
before do |
||||
recipient.members.destroy_all |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,109 @@ |
||||
#-- 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. |
||||
#++ |
||||
require 'spec_helper' |
||||
require_relative './create_from_journal_job_shared' |
||||
|
||||
describe Notifications::CreateFromModelService, 'news', with_settings: { journal_aggregation_time_minutes: 0 } do |
||||
subject(:call) do |
||||
described_class.new(journal).call(send_notifications) |
||||
end |
||||
|
||||
include_context 'with CreateFromJournalJob context' |
||||
|
||||
let(:journable) { FactoryBot.build_stubbed(:news) } |
||||
|
||||
let(:resource) { FactoryBot.create(:news, project: project) } |
||||
|
||||
# view_news is a public permission |
||||
let(:permissions) { [] } |
||||
let(:send_notifications) { true } |
||||
let(:journal) { resource.journals.last } |
||||
let(:author) { other_user } |
||||
|
||||
current_user { other_user } |
||||
|
||||
before do |
||||
recipient |
||||
end |
||||
|
||||
describe '#call' do |
||||
context 'with a newly created news do' do |
||||
context 'with the user having registered for all notifications' do |
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :subscribed, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user having registered for involved notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(involved: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for no notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
end |
||||
|
||||
context 'with an updated news' do |
||||
before do |
||||
resource.description = "Some new text to create a journal" |
||||
resource.save! |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications' do |
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,315 @@ |
||||
#-- 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. |
||||
#++ |
||||
require 'spec_helper' |
||||
require_relative './create_from_journal_job_shared' |
||||
|
||||
describe Notifications::CreateFromModelService, 'wiki', with_settings: { journal_aggregation_time_minutes: 0 } do |
||||
subject(:call) do |
||||
described_class.new(journal).call(send_notifications) |
||||
end |
||||
|
||||
include_context 'with CreateFromJournalJob context' |
||||
|
||||
shared_let(:project) { FactoryBot.create(:project) } |
||||
shared_let(:wiki) { FactoryBot.create(:wiki, project: project) } |
||||
|
||||
let(:permissions) { [:view_wiki_pages] } |
||||
let(:send_notifications) { true } |
||||
|
||||
let(:wiki_page) do |
||||
FactoryBot.create(:wiki_page, |
||||
wiki: wiki, |
||||
content: FactoryBot.build(:wiki_content, |
||||
author: other_user)) |
||||
end |
||||
let(:resource) { wiki_page.content } |
||||
let(:journal) { resource.journals.last } |
||||
let(:author) { other_user } |
||||
|
||||
current_user { other_user } |
||||
|
||||
before do |
||||
recipient |
||||
end |
||||
|
||||
describe '#perform' do |
||||
context 'with a newly created wiki page' do |
||||
context 'with the user having registered for all notifications' do |
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :subscribed, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user having registered for involved notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(involved: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for no notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and watching the wiki' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
wiki.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :watched, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user not having registered for watcher notifications and watching the wiki' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
wiki.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and not watching the wiki' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications but lacking permissions' do |
||||
let(:permissions) { [] } |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
end |
||||
|
||||
context 'with an updated wiki page' do |
||||
before do |
||||
resource.text = "Some new text to create a journal" |
||||
resource.save! |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications' do |
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :subscribed, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user having registered for involved notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(involved: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(involved: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for no notifications' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and watching the wiki' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
wiki.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :watched, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user not having registered for watcher notifications and watching the wiki' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
wiki.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and not watching the wiki nor the page' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for watcher notifications and watching the page' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false.merge(watched: true)), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false.merge(watched: true)) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
wiki_page.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates notification' do |
||||
let(:notification_channel_reasons) do |
||||
{ |
||||
read_ian: nil, |
||||
reason_ian: false, |
||||
read_mail: false, |
||||
reason_mail: :watched, |
||||
read_mail_digest: nil, |
||||
reason_mail_digest: false |
||||
} |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with the user not having registered for watcher notifications and watching the page' do |
||||
let(:recipient_notification_settings) do |
||||
[ |
||||
FactoryBot.build(:mail_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:in_app_notification_setting, **notification_settings_all_false), |
||||
FactoryBot.build(:mail_digest_notification_setting, **notification_settings_all_false) |
||||
] |
||||
end |
||||
|
||||
before do |
||||
wiki_page.watcher_users << recipient |
||||
end |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
|
||||
context 'with the user having registered for all notifications but lacking permissions' do |
||||
let(:permissions) { [] } |
||||
|
||||
it_behaves_like 'creates no notification' |
||||
end |
||||
end |
||||
end |
||||
end |
@ -1,85 +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. |
||||
#++ |
||||
require 'spec_helper' |
||||
|
||||
describe Notifications::JournalNotificationService do |
||||
let(:journal) { FactoryBot.build_stubbed(:journal, journable: journable) } |
||||
let(:send_mails) { true } |
||||
|
||||
shared_examples_for 'enqueues a notification' do |
||||
before do |
||||
# Freeze time |
||||
allow(Time) |
||||
.to receive(:current) |
||||
.and_return(Time.current) |
||||
|
||||
notification_set = double('notification set') |
||||
|
||||
expect(Notifications::JournalCompletedJob) |
||||
.to receive(:set) |
||||
.with(wait_until: Setting.journal_aggregation_time_minutes.to_i.minutes.from_now) |
||||
.and_return(notification_set) |
||||
|
||||
expect(notification_set) |
||||
.to receive(:perform_later) |
||||
.with(journal.id, send_mails) |
||||
end |
||||
|
||||
it 'fulfills expectations' do |
||||
described_class.call(journal, send_mails) |
||||
end |
||||
end |
||||
|
||||
shared_examples_for 'enqueues no notification' do |
||||
before do |
||||
expect(Notifications::JournalCompletedJob) |
||||
.not_to receive(:set) |
||||
end |
||||
|
||||
it 'fulfills expectations' do |
||||
described_class.call(journal, send_mails) |
||||
end |
||||
end |
||||
|
||||
context 'for a work package journal' do |
||||
let(:journable) { FactoryBot.build_stubbed(:stubbed_work_package) } |
||||
it_behaves_like 'enqueues a notification' |
||||
end |
||||
|
||||
context 'for a wiki content journal' do |
||||
let(:journable) { FactoryBot.build_stubbed(:wiki_content) } |
||||
it_behaves_like 'enqueues a notification' |
||||
end |
||||
|
||||
context 'for a news journal' do |
||||
let(:journable) { FactoryBot.build_stubbed(:news) } |
||||
it_behaves_like 'enqueues no notification' |
||||
end |
||||
end |
@ -1,240 +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. |
||||
#++ |
||||
require 'spec_helper' |
||||
|
||||
describe Notifications::JournalWikiMailService do |
||||
let(:project) do |
||||
FactoryBot.build_stubbed(:project).tap do |p| |
||||
allow(p) |
||||
.to receive(:notified_users) |
||||
.and_return([project_notified_user_with_permission, project_notified_user_wo_permission]) |
||||
end |
||||
end |
||||
let(:wiki) do |
||||
FactoryBot.build_stubbed(:wiki, project: project).tap do |w| |
||||
allow(w) |
||||
.to receive(:watcher_recipients) |
||||
.and_return([wiki_watcher_user]) |
||||
end |
||||
end |
||||
let(:wiki_page) do |
||||
FactoryBot.build_stubbed(:wiki_page, wiki: wiki).tap do |w| |
||||
allow(w) |
||||
.to receive(:watcher_recipients) |
||||
.and_return([wiki_page_watcher_user]) |
||||
end |
||||
end |
||||
let(:wiki_content) do |
||||
FactoryBot.build_stubbed(:wiki_content, page: wiki_page).tap do |wc| |
||||
allow(wc) |
||||
.to receive(:visible?) |
||||
.with(project_notified_user_with_permission) |
||||
.and_return(true) |
||||
|
||||
allow(wc) |
||||
.to receive(:visible?) |
||||
.with(project_notified_user_wo_permission) |
||||
.and_return(false) |
||||
end |
||||
end |
||||
let(:journal) do |
||||
FactoryBot.build_stubbed(:wiki_content_journal, journable: wiki_content, user: current_user).tap do |j| |
||||
allow(j) |
||||
.to receive(:initial?) |
||||
.and_return(journal_initial) |
||||
|
||||
allow(j) |
||||
.to receive(:noop?) |
||||
.and_return(journal_noop) |
||||
end |
||||
end |
||||
let(:journal_initial) { true } |
||||
let(:journal_noop) { false } |
||||
|
||||
let(:notification_setting) { %w(wiki_content_added wiki_content_updated) } |
||||
|
||||
let(:current_user) do |
||||
FactoryBot.build_stubbed(:user) |
||||
end |
||||
|
||||
let(:project_notified_user_with_permission) do |
||||
FactoryBot.build_stubbed(:user) |
||||
end |
||||
|
||||
let(:project_notified_user_wo_permission) do |
||||
FactoryBot.build_stubbed(:user) |
||||
end |
||||
|
||||
let(:wiki_watcher_user) do |
||||
FactoryBot.build_stubbed(:user) |
||||
end |
||||
|
||||
let(:wiki_page_watcher_user) do |
||||
FactoryBot.build_stubbed(:user) |
||||
end |
||||
|
||||
context '.call' do |
||||
let(:subject) { described_class.call(journal, send_mails) } |
||||
let(:send_mails) { true } |
||||
|
||||
before do |
||||
allow(Setting).to receive(:notified_events).and_return(notification_setting) |
||||
end |
||||
|
||||
shared_examples_for 'sends no mails' do |
||||
it 'sends no mails' do |
||||
expect(UserMailer) |
||||
.not_to receive(:wiki_content_updated) |
||||
|
||||
expect(UserMailer) |
||||
.not_to receive(:wiki_content_added) |
||||
|
||||
subject |
||||
end |
||||
end |
||||
|
||||
context 'with the settings allowing email sending for newly added content' do |
||||
let(:notification_setting) { %w(wiki_content_added) } |
||||
|
||||
context 'for an initial journal' do |
||||
let(:journal_initial) { true } |
||||
|
||||
it 'sends mails to users listening on all changes and to watchers of the wiki' do |
||||
[project_notified_user_with_permission, wiki_watcher_user].each do |u| |
||||
mailer = double('mailer') |
||||
|
||||
expect(UserMailer) |
||||
.to receive(:wiki_content_added) |
||||
.with(u, wiki_content, current_user) |
||||
.and_return(mailer) |
||||
|
||||
expect(mailer) |
||||
.to receive(:deliver_later) |
||||
end |
||||
|
||||
subject |
||||
end |
||||
|
||||
context 'with send_mails set to false' do |
||||
let(:send_mails) { false } |
||||
|
||||
it_behaves_like 'sends no mails' |
||||
end |
||||
|
||||
context 'with perform_deliveries set to false' do |
||||
before do |
||||
allow(UserMailer) |
||||
.to receive(:perform_deliveries) |
||||
.and_return(false) |
||||
end |
||||
|
||||
it_behaves_like 'sends no mails' |
||||
end |
||||
|
||||
context 'with the journal being a noop' do |
||||
let(:journal_noop) { true } |
||||
|
||||
it_behaves_like 'sends no mails' |
||||
end |
||||
end |
||||
|
||||
context 'for a non initial journal' do |
||||
let(:journal_initial) { false } |
||||
|
||||
it_behaves_like 'sends no mails' |
||||
end |
||||
end |
||||
|
||||
context 'with the settings allowing email sending for updated content' do |
||||
let(:notification_setting) { %w(wiki_content_updated) } |
||||
|
||||
context 'for a non initial journal' do |
||||
let(:journal_initial) { false } |
||||
|
||||
it 'sends mails to users listening on all changes and to watchers of the wiki' do |
||||
[project_notified_user_with_permission, wiki_watcher_user, wiki_page_watcher_user].each do |u| |
||||
mailer = double('mailer') |
||||
|
||||
expect(UserMailer) |
||||
.to receive(:wiki_content_updated) |
||||
.with(u, wiki_content, current_user) |
||||
.and_return(mailer) |
||||
|
||||
expect(mailer) |
||||
.to receive(:deliver_later) |
||||
end |
||||
|
||||
subject |
||||
end |
||||
|
||||
context 'with send_mails set to false' do |
||||
let(:send_mails) { false } |
||||
|
||||
it_behaves_like 'sends no mails' |
||||
end |
||||
|
||||
context 'with perform_deliveries set to false' do |
||||
before do |
||||
allow(UserMailer) |
||||
.to receive(:perform_deliveries) |
||||
.and_return(false) |
||||
end |
||||
|
||||
it_behaves_like 'sends no mails' |
||||
end |
||||
|
||||
context 'with the journal being a noop' do |
||||
let(:journal_noop) { true } |
||||
|
||||
it_behaves_like 'sends no mails' |
||||
end |
||||
end |
||||
|
||||
context 'for an initial journal' do |
||||
let(:journal_initial) { true } |
||||
|
||||
it_behaves_like 'sends no mails' |
||||
end |
||||
end |
||||
end |
||||
|
||||
it 'listener is subscribed' do |
||||
journal = double('journal') |
||||
send_mail = true |
||||
|
||||
expect(Notifications::JournalWikiMailService) |
||||
.to receive(:call) |
||||
.with(journal, send_mail) |
||||
|
||||
OpenProject::Notifications.send(OpenProject::Events::AGGREGATED_WIKI_JOURNAL_READY, |
||||
journal: journal, |
||||
send_mail: send_mail) |
||||
end |
||||
end |
@ -0,0 +1,412 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe Notifications::MailService, type: :model do |
||||
subject(:call) { instance.call } |
||||
|
||||
let(:recipient) do |
||||
FactoryBot.build_stubbed(:user) |
||||
end |
||||
let(:actor) do |
||||
FactoryBot.build_stubbed(:user) |
||||
end |
||||
let(:instance) { described_class.new(notification) } |
||||
|
||||
context 'with a work package journal notification' do |
||||
let(:journal) do |
||||
FactoryBot.build_stubbed(:work_package_journal).tap do |j| |
||||
allow(j) |
||||
.to receive(:initial?) |
||||
.and_return(journal_initial) |
||||
end |
||||
end |
||||
let(:read_ian) { false } |
||||
let(:notification) do |
||||
FactoryBot.build_stubbed(:notification, |
||||
journal: journal, |
||||
recipient: recipient, |
||||
actor: actor, |
||||
read_ian: read_ian) |
||||
end |
||||
let(:journal_initial) { false } |
||||
|
||||
let(:mail) do |
||||
mail = instance_double(ActionMailer::MessageDelivery) |
||||
|
||||
allow(UserMailer) |
||||
.to receive(:work_package_added) |
||||
.and_return(mail) |
||||
|
||||
allow(UserMailer) |
||||
.to receive(:work_package_updated) |
||||
.and_return(mail) |
||||
|
||||
allow(mail) |
||||
.to receive(:deliver_later) |
||||
|
||||
mail |
||||
end |
||||
|
||||
before do |
||||
mail |
||||
end |
||||
|
||||
context 'with the notification being for an initial journal' do |
||||
let(:journal_initial) { true } |
||||
|
||||
it 'sends a mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.to have_received(:work_package_added) |
||||
.with(recipient, |
||||
journal, |
||||
journal.user) |
||||
|
||||
expect(mail) |
||||
.to have_received(:deliver_later) |
||||
end |
||||
end |
||||
|
||||
context 'with the notification being for an update journal' do |
||||
let(:journal_initial) { false } |
||||
|
||||
it 'sends a mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.to have_received(:work_package_updated) |
||||
.with(recipient, |
||||
journal, |
||||
journal.user) |
||||
|
||||
expect(mail) |
||||
.to have_received(:deliver_later) |
||||
end |
||||
end |
||||
|
||||
context 'with the notification read in app already' do |
||||
let(:read_ian) { true } |
||||
|
||||
it 'sends no mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.not_to have_received(:work_package_added) |
||||
expect(UserMailer) |
||||
.not_to have_received(:work_package_updated) |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with a wiki_content journal notification' do |
||||
let(:journal) do |
||||
FactoryBot.build_stubbed(:wiki_content_journal, |
||||
journable: FactoryBot.build_stubbed(:wiki_content)).tap do |j| |
||||
allow(j) |
||||
.to receive(:initial?) |
||||
.and_return(journal_initial) |
||||
end |
||||
end |
||||
let(:read_ian) { false } |
||||
let(:notification) do |
||||
FactoryBot.build_stubbed(:notification, |
||||
journal: journal, |
||||
recipient: recipient, |
||||
actor: actor, |
||||
read_ian: read_ian) |
||||
end |
||||
let(:notification_setting) { %w(wiki_content_added wiki_content_updated) } |
||||
let(:mail) do |
||||
mail = instance_double(ActionMailer::MessageDelivery) |
||||
|
||||
allow(UserMailer) |
||||
.to receive(:wiki_content_added) |
||||
.and_return(mail) |
||||
|
||||
allow(UserMailer) |
||||
.to receive(:wiki_content_updated) |
||||
.and_return(mail) |
||||
|
||||
allow(mail) |
||||
.to receive(:deliver_later) |
||||
|
||||
mail |
||||
end |
||||
let(:journal_initial) { false } |
||||
|
||||
before do |
||||
mail |
||||
|
||||
allow(Setting).to receive(:notified_events).and_return(notification_setting) |
||||
end |
||||
|
||||
context 'with the notification being for an initial journal' do |
||||
let(:journal_initial) { true } |
||||
|
||||
it 'sends a mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.to have_received(:wiki_content_added) |
||||
.with(recipient, |
||||
journal.journable, |
||||
journal.user) |
||||
|
||||
expect(mail) |
||||
.to have_received(:deliver_later) |
||||
end |
||||
end |
||||
|
||||
context 'with the notification being for an initial journal but the event is disabled' do |
||||
let(:journal_initial) { true } |
||||
let(:notification_setting) { %w(wiki_content_updated) } |
||||
|
||||
it 'sends no mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.not_to have_received(:wiki_content_added) |
||||
end |
||||
end |
||||
|
||||
context 'with the notification being for an update journal' do |
||||
let(:journal_initial) { false } |
||||
|
||||
it 'sends a mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.to have_received(:wiki_content_updated) |
||||
.with(recipient, |
||||
journal.journable, |
||||
journal.user) |
||||
|
||||
expect(mail) |
||||
.to have_received(:deliver_later) |
||||
end |
||||
end |
||||
|
||||
context 'with the notification being for an update journal but the event is disabled' do |
||||
let(:journal_initial) { false } |
||||
let(:notification_setting) { %w(wiki_content_added) } |
||||
|
||||
it 'sends no mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.not_to have_received(:wiki_content_updated) |
||||
end |
||||
end |
||||
|
||||
context 'with the notification read in app already' do |
||||
let(:read_ian) { true } |
||||
|
||||
it 'sends no mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.not_to have_received(:wiki_content_added) |
||||
expect(UserMailer) |
||||
.not_to have_received(:wiki_content_updated) |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with a news journal notification' do |
||||
let(:journal) do |
||||
FactoryBot.build_stubbed(:news_journal, |
||||
journable: FactoryBot.build_stubbed(:news)).tap do |j| |
||||
allow(j) |
||||
.to receive(:initial?) |
||||
.and_return(journal_initial) |
||||
end |
||||
end |
||||
let(:notification) do |
||||
FactoryBot.build_stubbed(:notification, |
||||
journal: journal, |
||||
recipient: recipient, |
||||
actor: actor) |
||||
end |
||||
let(:notification_setting) { %w(news_added) } |
||||
let(:mail) do |
||||
mail = instance_double(ActionMailer::MessageDelivery) |
||||
|
||||
allow(UserMailer) |
||||
.to receive(:news_added) |
||||
.and_return(mail) |
||||
|
||||
allow(mail) |
||||
.to receive(:deliver_later) |
||||
|
||||
mail |
||||
end |
||||
let(:journal_initial) { false } |
||||
|
||||
before do |
||||
mail |
||||
|
||||
allow(Setting).to receive(:notified_events).and_return(notification_setting) |
||||
end |
||||
|
||||
context 'with the notification being for an initial journal' do |
||||
let(:journal_initial) { true } |
||||
|
||||
it 'sends a mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.to have_received(:news_added) |
||||
.with(recipient, |
||||
journal.journable, |
||||
journal.user) |
||||
|
||||
expect(mail) |
||||
.to have_received(:deliver_later) |
||||
end |
||||
end |
||||
|
||||
context 'with the notification being for an initial journal but the event is disabled' do |
||||
let(:journal_initial) { true } |
||||
let(:notification_setting) { %w() } |
||||
|
||||
it 'sends no mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.not_to have_received(:news_added) |
||||
end |
||||
end |
||||
|
||||
# This case should not happen as no notification is created in this case that would |
||||
# trigger the NotificationJob. But as this might change, this test case is in place. |
||||
context 'with the notification being for an update journal' do |
||||
let(:journal_initial) { false } |
||||
|
||||
it 'sends no mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.not_to have_received(:news_added) |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with a message journal notification' do |
||||
let(:journal) do |
||||
FactoryBot.build_stubbed(:message_journal, |
||||
journable: FactoryBot.build_stubbed(:message)) |
||||
end |
||||
let(:read_ian) { false } |
||||
let(:notification) do |
||||
FactoryBot.build_stubbed(:notification, |
||||
journal: journal, |
||||
resource: journal.journable, |
||||
recipient: recipient, |
||||
actor: actor, |
||||
read_ian: read_ian) |
||||
end |
||||
let(:notification_setting) { %w(message_posted) } |
||||
let(:mail) do |
||||
mail = instance_double(ActionMailer::MessageDelivery) |
||||
|
||||
allow(UserMailer) |
||||
.to receive(:message_posted) |
||||
.and_return(mail) |
||||
|
||||
allow(mail) |
||||
.to receive(:deliver_later) |
||||
|
||||
mail |
||||
end |
||||
|
||||
before do |
||||
mail |
||||
|
||||
allow(Setting).to receive(:notified_events).and_return(notification_setting) |
||||
end |
||||
|
||||
it 'sends a mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.to have_received(:message_posted) |
||||
.with(recipient, |
||||
journal.journable, |
||||
actor) |
||||
|
||||
expect(mail) |
||||
.to have_received(:deliver_later) |
||||
end |
||||
|
||||
context 'with the event being disabled' do |
||||
let(:notification_setting) { %w(wiki_content_updated) } |
||||
|
||||
it 'sends no mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.not_to have_received(:message_posted) |
||||
end |
||||
end |
||||
|
||||
context 'with the notification read in app already' do |
||||
let(:read_ian) { true } |
||||
|
||||
it 'sends no mail' do |
||||
call |
||||
|
||||
expect(UserMailer) |
||||
.not_to have_received(:message_posted) |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with a different journal notification' do |
||||
# This is actually not supported by now but serves as a test |
||||
let(:journal) do |
||||
FactoryBot.build_stubbed(:journal, |
||||
journable: FactoryBot.build_stubbed(:user)) |
||||
end |
||||
let(:notification) do |
||||
FactoryBot.build_stubbed(:notification, |
||||
journal: journal, |
||||
recipient: recipient, |
||||
actor: actor) |
||||
end |
||||
|
||||
it 'raises an error' do |
||||
expect { call } |
||||
.to raise_error(ArgumentError) |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,158 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe Journals::CompletedJob, type: :model do |
||||
let(:send_mail) { true } |
||||
|
||||
let(:journal) do |
||||
FactoryBot.build_stubbed(:journal, journable: journable).tap do |j| |
||||
allow(Journal) |
||||
.to receive(:find) |
||||
.with(j.id.to_s) |
||||
.and_return(j) |
||||
allow(Journal) |
||||
.to receive(:find_by) |
||||
.with(id: j.id) |
||||
.and_return(j) |
||||
allow(Journal) |
||||
.to receive(:exists?) |
||||
.with(id: j.id) |
||||
.and_return(true) |
||||
end |
||||
end |
||||
|
||||
describe '.schedule' do |
||||
subject { described_class.schedule(journal, send_mail) } |
||||
|
||||
shared_examples_for 'enqueues a JournalCompletedJob' do |
||||
before do |
||||
allow(Time) |
||||
.to receive(:current) |
||||
.and_return(Time.current) |
||||
end |
||||
|
||||
it 'enqueues a JournalCompletedJob' do |
||||
expect { subject } |
||||
.to have_enqueued_job(described_class) |
||||
.at(Setting.journal_aggregation_time_minutes.to_i.minutes.from_now) |
||||
.with(journal.id, |
||||
send_mail) |
||||
end |
||||
end |
||||
|
||||
shared_examples_for 'enqueues no job' do |
||||
it 'enqueues no JournalCompletedJob' do |
||||
expect { subject } |
||||
.not_to have_enqueued_job(described_class) |
||||
end |
||||
end |
||||
|
||||
context 'with a work_package' do |
||||
let(:journable) { FactoryBot.build_stubbed(:work_package) } |
||||
|
||||
it_behaves_like 'enqueues a JournalCompletedJob' |
||||
end |
||||
|
||||
context 'with a wiki page' do |
||||
let(:journable) { FactoryBot.build_stubbed(:wiki_content) } |
||||
|
||||
it_behaves_like 'enqueues a JournalCompletedJob' |
||||
end |
||||
|
||||
context 'with a news' do |
||||
let(:journable) { FactoryBot.build_stubbed(:news) } |
||||
|
||||
it_behaves_like 'enqueues a JournalCompletedJob' |
||||
end |
||||
end |
||||
|
||||
describe '#perform' do |
||||
subject { described_class.new.perform(journal.id, send_mail) } |
||||
|
||||
shared_examples_for 'sends a notification' do |event| |
||||
it 'sends a notification' do |
||||
allow(OpenProject::Notifications) |
||||
.to receive(:send) |
||||
|
||||
subject |
||||
|
||||
expect(OpenProject::Notifications) |
||||
.to have_received(:send) |
||||
.with(event, |
||||
journal: journal, |
||||
send_mail: send_mail) |
||||
end |
||||
end |
||||
|
||||
context 'with a work packages' do |
||||
let(:journable) { FactoryBot.build_stubbed(:work_package) } |
||||
|
||||
it_behaves_like 'sends a notification', |
||||
OpenProject::Events::AGGREGATED_WORK_PACKAGE_JOURNAL_READY |
||||
end |
||||
|
||||
context 'with wiki page content' do |
||||
let(:journable) { FactoryBot.build_stubbed(:wiki_content) } |
||||
|
||||
it_behaves_like 'sends a notification', |
||||
OpenProject::Events::AGGREGATED_WIKI_JOURNAL_READY |
||||
end |
||||
|
||||
context 'with a news' do |
||||
let(:journable) { FactoryBot.build_stubbed(:news) } |
||||
|
||||
it_behaves_like 'sends a notification', |
||||
OpenProject::Events::AGGREGATED_NEWS_JOURNAL_READY |
||||
end |
||||
|
||||
context 'with a non non-existant journal' do |
||||
let(:journable) { FactoryBot.build_stubbed(:work_package) } |
||||
|
||||
before do |
||||
allow(Journal) |
||||
.to receive(:find_by) |
||||
.with(id: journal.id) |
||||
.and_return(nil) |
||||
end |
||||
|
||||
it 'sends no notification' do |
||||
allow(OpenProject::Notifications) |
||||
.to receive(:send) |
||||
|
||||
subject |
||||
|
||||
expect(OpenProject::Notifications) |
||||
.not_to have_received(:send) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -1,173 +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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe Mails::WorkPackageJob, type: :model do |
||||
let(:project) { FactoryBot.create(:project) } |
||||
let(:role) { FactoryBot.create(:role, permissions: [:view_work_packages]) } |
||||
let(:recipient) do |
||||
FactoryBot.create(:user, member_in_project: project, member_through_role: role) |
||||
end |
||||
let(:author) { FactoryBot.create(:user) } |
||||
let(:work_package) do |
||||
FactoryBot.create(:work_package, |
||||
project: project, |
||||
author: author) |
||||
end |
||||
let(:journal) { work_package.journals.first } |
||||
let(:instance) { described_class.new } |
||||
subject { instance.perform(journal.id, recipient.id, author.id) } |
||||
|
||||
before do |
||||
# make sure no actual calls make it into the UserMailer |
||||
allow(UserMailer).to receive(:work_package_added).and_return(double('mail', deliver_now: nil)) |
||||
allow(UserMailer).to receive(:work_package_updated).and_return(double('mail', deliver_now: nil)) |
||||
end |
||||
|
||||
it 'sends a mail' do |
||||
expect(UserMailer) |
||||
.to receive(:work_package_added) |
||||
.with(recipient, an_instance_of(Journal), author) |
||||
subject |
||||
end |
||||
|
||||
context 'non-existant journal' do |
||||
before do |
||||
journal.destroy |
||||
end |
||||
|
||||
it 'sends no mail' do |
||||
expect(UserMailer).not_to receive(:work_package_added) |
||||
subject |
||||
end |
||||
end |
||||
|
||||
context 'non-existant author' do |
||||
before do |
||||
author.destroy |
||||
end |
||||
|
||||
it 'sends a mail' do |
||||
expect(UserMailer).to receive(:work_package_added) |
||||
subject |
||||
end |
||||
|
||||
it 'uses the deleted user as author' do |
||||
expect(UserMailer) |
||||
.to receive(:work_package_added) |
||||
.with(anything, anything, DeletedUser.first) |
||||
|
||||
subject |
||||
end |
||||
end |
||||
|
||||
context 'outdated journal' do |
||||
before do |
||||
# make sure there is a later journal, that supersedes the original one |
||||
work_package.subject = 'changed subject' |
||||
work_package.save! |
||||
end |
||||
|
||||
it 'raises no observable error' do |
||||
expect { subject }.not_to raise_error |
||||
end |
||||
end |
||||
|
||||
context 'update journal' do |
||||
let(:journal) { work_package.journals.last } |
||||
|
||||
before do |
||||
work_package.add_journal(FactoryBot.create(:user), 'a comment') |
||||
work_package.save! |
||||
end |
||||
|
||||
it 'sends an update mail' do |
||||
expect(UserMailer).to receive(:work_package_updated) |
||||
subject |
||||
end |
||||
|
||||
it 'sends a mail for the journal' do |
||||
expected = Journal.last |
||||
expect(UserMailer).to receive(:work_package_updated) do |_recipient, journal, _author| |
||||
expect(journal.id).to eq expected.id |
||||
expect(journal.notes_id).to eq expected.notes_id |
||||
|
||||
double('mail', deliver_now: nil) |
||||
end |
||||
subject |
||||
end |
||||
end |
||||
|
||||
describe 'impersonation' do |
||||
describe 'the recipient should become the current user during mail creation' do |
||||
before do |
||||
expect(UserMailer).to receive(:work_package_added) do |
||||
expect(User.current).to eql(recipient) |
||||
double('mail', deliver_now: nil) |
||||
end |
||||
end |
||||
|
||||
it { subject } |
||||
end |
||||
|
||||
context 'for a known current user' do |
||||
let(:current_user) { FactoryBot.create(:user) } |
||||
|
||||
it 'resets to the previous current user after running' do |
||||
User.current = current_user |
||||
subject |
||||
expect(User.current).to eql(current_user) |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'exceptions during delivery' do |
||||
before do |
||||
mail = double('mail') |
||||
allow(mail).to receive(:deliver_now).and_raise(SocketError) |
||||
expect(UserMailer).to receive(:work_package_added).and_return(mail) |
||||
end |
||||
|
||||
it 'raises the error' do |
||||
expect { subject }.to raise_error(SocketError) |
||||
end |
||||
end |
||||
|
||||
describe 'exceptions during rendering' do |
||||
before do |
||||
expect(UserMailer).to receive(:work_package_added).and_raise('not today!') |
||||
end |
||||
|
||||
it 'swallows the error' do |
||||
expect { subject }.not_to raise_error |
||||
end |
||||
end |
||||
end |
@ -1,151 +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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe Notifications::JournalCompletedJob, type: :model do |
||||
let(:project) { FactoryBot.create(:project) } |
||||
let(:permissions) { [:view_work_packages] } |
||||
let(:recipient) do |
||||
FactoryBot.create(:user, member_in_project: project, member_with_permissions: permissions, login: "johndoe") |
||||
end |
||||
let(:author) { FactoryBot.create(:user, login: "marktwain") } |
||||
let(:send_mail) { true } |
||||
|
||||
subject { described_class.new.perform(journal.id, send_mail) } |
||||
|
||||
before do |
||||
# make sure no other calls are made due to WP creation/update |
||||
allow(OpenProject::Notifications).to receive(:send) # ... and do nothing |
||||
end |
||||
|
||||
context 'for work packages' do |
||||
let(:work_package) do |
||||
FactoryBot.create(:work_package, |
||||
project: project, |
||||
author: author, |
||||
assigned_to: recipient) |
||||
end |
||||
let(:journal) { journal1 } |
||||
let(:journal1) { work_package.journals.first } |
||||
let(:journal2) do |
||||
work_package.add_journal author, 'something I have to say' |
||||
work_package.save(validate: false) |
||||
work_package.journals.last |
||||
end |
||||
|
||||
shared_examples_for 'sends notification' do |
||||
it 'sends a notification' do |
||||
expect(OpenProject::Notifications) |
||||
.to receive(:send) |
||||
.with(OpenProject::Events::AGGREGATED_WORK_PACKAGE_JOURNAL_READY, |
||||
journal: an_instance_of(Journal), |
||||
send_mail: send_mail) |
||||
|
||||
subject |
||||
end |
||||
end |
||||
|
||||
shared_examples_for 'sends no notification' do |
||||
it 'sends no notification' do |
||||
expect(OpenProject::Notifications) |
||||
.not_to receive(:send) |
||||
|
||||
subject |
||||
end |
||||
end |
||||
|
||||
it_behaves_like 'sends notification' |
||||
|
||||
context 'non-existant journal' do |
||||
before do |
||||
journal.destroy |
||||
end |
||||
|
||||
it_behaves_like 'sends no notification' |
||||
end |
||||
|
||||
describe 'journal creation' do |
||||
context 'with the work package being created' do |
||||
before do |
||||
FactoryBot.create(:work_package, project: project) |
||||
end |
||||
|
||||
it_behaves_like 'sends notification' |
||||
end |
||||
|
||||
context 'with the work package being updated' do |
||||
before do |
||||
work_package.add_journal(author) |
||||
work_package.subject = 'A change to the issue' |
||||
work_package.save!(validate: false) |
||||
end |
||||
|
||||
it_behaves_like 'sends notification' |
||||
end |
||||
|
||||
context 'with the journal being updated with a note' do |
||||
before do |
||||
work_package.add_journal(author, 'This update has a note') |
||||
work_package.save!(validate: false) |
||||
end |
||||
|
||||
it_behaves_like 'sends notification' |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'with wiki page content' do |
||||
let(:wiki_page_content) do |
||||
wiki = FactoryBot.create(:wiki, |
||||
project: project) |
||||
|
||||
FactoryBot.create(:wiki_page_with_content, wiki: wiki).content |
||||
end |
||||
|
||||
let(:journal) { journal1 } |
||||
let(:journal1) { wiki_page_content.journals.first } |
||||
let(:journal2) do |
||||
wiki_page_content.add_journal author, 'something I have to say' |
||||
wiki_page_content.save(validate: false) |
||||
wiki_page_content.journals.last |
||||
end |
||||
|
||||
it 'sends a notification' do |
||||
expect(OpenProject::Notifications) |
||||
.to receive(:send) |
||||
.with(OpenProject::Events::AGGREGATED_WIKI_JOURNAL_READY, |
||||
journal: an_instance_of(Journal), |
||||
send_mail: send_mail) |
||||
|
||||
subject |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,144 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
require 'spec_helper' |
||||
|
||||
describe Notifications::WorkflowJob, type: :model do |
||||
subject(:perform_job) do |
||||
described_class.new.perform(state, *arguments) |
||||
end |
||||
|
||||
let(:send_notification) { true } |
||||
|
||||
let(:notifications) do |
||||
[FactoryBot.build_stubbed(:notification), |
||||
FactoryBot.build_stubbed(:notification)] |
||||
end |
||||
|
||||
describe '#perform' do |
||||
context 'with the :create_notifications state' do |
||||
let(:state) { :create_notifications } |
||||
let(:arguments) { [resource, send_notification] } |
||||
let(:resource) { FactoryBot.build_stubbed(:comment) } |
||||
|
||||
let!(:create_service) do |
||||
service_instance = instance_double(Notifications::CreateFromModelService) |
||||
service_result = instance_double(ServiceResult) |
||||
|
||||
allow(Notifications::CreateFromModelService) |
||||
.to receive(:new) |
||||
.with(resource) |
||||
.and_return(service_instance) |
||||
|
||||
allow(service_instance) |
||||
.to receive(:call) |
||||
.with(send_notification) |
||||
.and_return(service_result) |
||||
|
||||
allow(service_result) |
||||
.to receive(:all_results) |
||||
.and_return(notifications) |
||||
|
||||
service_instance |
||||
end |
||||
|
||||
it 'calls the service to create notifications' do |
||||
perform_job |
||||
|
||||
expect(create_service) |
||||
.to have_received(:call) |
||||
.with(send_notification) |
||||
end |
||||
|
||||
it 'schedules a delayed WorkflowJob' do |
||||
allow(Time) |
||||
.to receive(:current) |
||||
.and_return(Time.current) |
||||
|
||||
expected_time = Time.current + |
||||
Setting.notification_email_delay_minutes.minutes + |
||||
Setting.journal_aggregation_time_minutes.to_i.minutes |
||||
|
||||
expect { perform_job } |
||||
.to enqueue_job(described_class) |
||||
.with(:send_mails, *notifications.map(&:id)) |
||||
.at(expected_time) |
||||
end |
||||
end |
||||
|
||||
context 'with the :send_mails state' do |
||||
let(:state) { :send_mails } |
||||
let(:arguments) { notifications.map(&:id) } |
||||
|
||||
let!(:mail_service) do |
||||
service_instance = instance_double(Notifications::MailService, |
||||
call: nil) |
||||
|
||||
allow(Notifications::MailService) |
||||
.to receive(:new) |
||||
.with(notifications.first) |
||||
.and_return(service_instance) |
||||
|
||||
service_instance |
||||
end |
||||
|
||||
let!(:digest_job) do |
||||
allow(Mails::DigestJob) |
||||
.to receive(:schedule) |
||||
end |
||||
|
||||
before do |
||||
scope = class_double(Notification, |
||||
unread_mail: [notifications.first], |
||||
unread_mail_digest: [notifications.last]) |
||||
|
||||
allow(Notification) |
||||
.to receive(:where) |
||||
.with(id: notifications.map(&:id)) |
||||
.and_return(scope) |
||||
end |
||||
|
||||
it 'sends mails for all notifications that are marked to send mails' do |
||||
perform_job |
||||
|
||||
expect(mail_service) |
||||
.to have_received(:call) |
||||
end |
||||
|
||||
it 'schedules a digest job for all notifications that are marked for the digest' do |
||||
perform_job |
||||
|
||||
expect(Mails::DigestJob) |
||||
.to have_received(:schedule) |
||||
.with(notifications.last) |
||||
end |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue