OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
openproject/lib/plugins/acts_as_watchable/lib/acts_as_watchable.rb

175 lines
5.9 KiB

#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# 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-2017 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.
#++
#-- encoding: UTF-8
# ActsAsWatchable
module Redmine
module Acts
module Watchable
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
# Marks an ActiveRecord::Model as watchable
# A watchable model has association with users (watchers) who wish to
# be informed of changes on it.
#
# This also creates the routes necessary for watching/unwatching by
# adding the model's name to routes. This e.g leads to the following
# routes when marking issues as watchable:
# POST: issues/1/watch
# DELETE: issues/1/unwatch
# GET/POST: issues/1/watchers/new
# DELETE: issues/1/watchers/1
#
# params:
# options:
# permission: overrides the permission used to determine whether a user
# is allowed to watch
def acts_as_watchable(options = {})
return if included_modules.include?(Redmine::Acts::Watchable::InstanceMethods)
acts_as_watchable_enforce_project_association
class_eval do
has_many :watchers, as: :watchable, dependent: :delete_all, validate: false
has_many :watcher_users, through: :watchers, source: :user, validate: false
scope :watched_by, ->(user_id) {
includes(:watchers)
.where(watchers: { user_id: user_id })
}
class_attribute :acts_as_watchable_options
self.acts_as_watchable_options = options
end
send :prepend, Redmine::Acts::Watchable::InstanceMethods
end
def acts_as_watchable_enforce_project_association
unless reflect_on_association(:project)
message = <<-MESSAGE
The #{self} model does not have an association to the Project model.
acts_as_watchable requires the including model to have such an association.
If no direct association exists, consider adding a
has_one :project, through: ...
association.
MESSAGE
raise message
end
end
end
module InstanceMethods
def self.prepended(base)
base.extend ClassMethods
end
def possible_watcher?(user)
user.allowed_to?(self.class.acts_as_watchable_permission, project)
end
# Returns all users that could potentially be watchers.
# This includes those already added as watchers.
#
# Admins are excluded at least for non public projects
# because while they have the right to be added as watchers having
# them pop up in every project would be weird.
def possible_watcher_users
users = User
.active_or_registered
if project.is_public?
users.allowed(self.class.acts_as_watchable_permission, project)
else
users.allowed_members(self.class.acts_as_watchable_permission, project)
end
end
# Returns an array of users that are proposed as watchers
def addable_watcher_users
possible_watcher_users.where.not(id: watcher_users.pluck(:id))
end
# Adds user as a watcher
def add_watcher(user)
watchers << Watcher.new(user: user, watchable: self)
end
# Removes user from the watchers list
def remove_watcher(user)
return nil unless user && user.is_a?(User)
watchers_to_delete = watchers.find_all { |watcher| watcher.user == user }
watchers_to_delete.each(&:delete)
watchers.reload
watchers_to_delete.count
end
# Adds/removes watcher
def set_watcher(user, watching = true)
watching ? add_watcher(user) : remove_watcher(user)
end
# Overrides watcher_user_ids= to make user_ids uniq
def watcher_user_ids=(user_ids)
if user_ids.is_a?(Array)
user_ids = user_ids.uniq
end
super
end
# Returns true if object is watched by +user+
def watched_by?(user)
user.present? &&
(watchers.loaded? && watchers.map(&:user_id).any? { |uid| uid == user.id } ||
watcher_user_ids.any? { |uid| uid == user.id })
end
# Returns an array of watchers
def watcher_recipients
possible_watcher_users & watcher_users.active.where.not(mail_notification: 'none')
end
module ClassMethods
def acts_as_watchable_permission
acts_as_watchable_options[:permission] || "view_#{name.underscore.pluralize}".to_sym
end
end
end
end
end
end