##// END OF EJS Templates
Adds permissions for viewing the watcher list and adding new watchers on the issue detail view (#398)....
Jean-Philippe Lang -
r1711:a332e0a4febe
parent child
Show More
@@ -0,0 +1,25
1 <div class="contextual">
2 <%= link_to_remote l(:button_add),
3 :url => {:controller => 'watchers',
4 :action => 'new',
5 :object_type => watched.class.name.underscore,
6 :object_id => watched} if User.current.allowed_to?(:add_issue_watchers, @project) %>
7 </div>
8
9 <p><strong><%= l(:label_issue_watchers) %></strong></p>
10 <%= watchers_list(watched) %>
11
12 <% unless @watcher.nil? %>
13 <% remote_form_for(:watcher, @watcher,
14 :url => {:controller => 'watchers',
15 :action => 'new',
16 :object_type => watched.class.name.underscore,
17 :object_id => watched},
18 :method => :post,
19 :html => {:id => 'new-watcher-form'}) do |f| %>
20 <p><%= f.select :user_id, (watched.addable_watcher_users.collect {|m| [m.name, m.id]}), :prompt => true %>
21
22 <%= submit_tag l(:button_add) %>
23 <%= toggle_link l(:button_cancel), 'new-watcher-form'%></p>
24 <% end %>
25 <% end %>
@@ -0,0 +1,6
1 ---
2 watchers_001:
3 watchable_type: Issue
4 watchable_id: 2
5 user_id: 3
6 No newline at end of file
@@ -0,0 +1,70
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require File.dirname(__FILE__) + '/../test_helper'
19 require 'watchers_controller'
20
21 # Re-raise errors caught by the controller.
22 class WatchersController; def rescue_action(e) raise e end; end
23
24 class WatchersControllerTest < Test::Unit::TestCase
25 fixtures :projects, :users, :roles, :members, :enabled_modules,
26 :issues, :trackers, :projects_trackers, :issue_statuses, :enumerations, :watchers
27
28 def setup
29 @controller = WatchersController.new
30 @request = ActionController::TestRequest.new
31 @response = ActionController::TestResponse.new
32 User.current = nil
33 end
34
35 def test_get_watch_should_be_invalid
36 @request.session[:user_id] = 3
37 get :watch, :object_type => 'issue', :object_id => '1'
38 assert_response 405
39 end
40
41 def test_watch
42 @request.session[:user_id] = 3
43 assert_difference('Watcher.count') do
44 xhr :post, :watch, :object_type => 'issue', :object_id => '1'
45 assert_response :success
46 assert_select_rjs :replace_html, 'watcher'
47 end
48 assert Issue.find(1).watched_by?(User.find(3))
49 end
50
51 def test_unwatch
52 @request.session[:user_id] = 3
53 assert_difference('Watcher.count', -1) do
54 xhr :post, :unwatch, :object_type => 'issue', :object_id => '2'
55 assert_response :success
56 assert_select_rjs :replace_html, 'watcher'
57 end
58 assert !Issue.find(1).watched_by?(User.find(3))
59 end
60
61 def test_new_watcher
62 @request.session[:user_id] = 2
63 assert_difference('Watcher.count') do
64 xhr :post, :new, :object_type => 'issue', :object_id => '2', :watcher => {:user_id => '4'}
65 assert_response :success
66 assert_select_rjs :replace_html, 'watchers'
67 end
68 assert Issue.find(2).watched_by?(User.find(4))
69 end
70 end
@@ -17,30 +17,38
17 17
18 18 class WatchersController < ApplicationController
19 19 layout 'base'
20 before_filter :require_login, :find_project, :check_project_privacy
20 before_filter :find_project
21 before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
22 before_filter :authorize, :only => :new
21 23
22 def add
23 user = User.current
24 @watched.add_watcher(user)
25 respond_to do |format|
26 format.html { redirect_to :back }
27 format.js { render(:update) {|page| page.replace_html 'watcher', watcher_link(@watched, user)} }
28 end
29 rescue RedirectBackError
30 render :text => 'Watcher added.', :layout => true
24 verify :method => :post,
25 :only => [ :watch, :unwatch ],
26 :render => { :nothing => true, :status => :method_not_allowed }
27
28 def watch
29 set_watcher(User.current, true)
30 end
31
32 def unwatch
33 set_watcher(User.current, false)
31 34 end
32 35
33 def remove
34 user = User.current
35 @watched.remove_watcher(user)
36 def new
37 @watcher = Watcher.new(params[:watcher])
38 @watcher.watchable = @watched
39 @watcher.save if request.post?
36 40 respond_to do |format|
37 41 format.html { redirect_to :back }
38 format.js { render(:update) {|page| page.replace_html 'watcher', watcher_link(@watched, user)} }
42 format.js do
43 render :update do |page|
44 page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
45 end
46 end
39 47 end
40 rescue RedirectBackError
41 render :text => 'Watcher removed.', :layout => true
48 rescue ::ActionController::RedirectBackError
49 render :text => 'Watcher added.', :layout => true
42 50 end
43
51
44 52 private
45 53 def find_project
46 54 klass = Object.const_get(params[:object_type].camelcase)
@@ -50,4 +58,14 private
50 58 rescue
51 59 render_404
52 60 end
61
62 def set_watcher(user, watching)
63 @watched.set_watcher(user, watching)
64 respond_to do |format|
65 format.html { redirect_to :back }
66 format.js { render(:update) {|page| page.replace_html 'watcher', watcher_link(@watched, user)} }
67 end
68 rescue ::ActionController::RedirectBackError
69 render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true
70 end
53 71 end
@@ -24,7 +24,7 module WatchersHelper
24 24 return '' unless user && user.logged? && object.respond_to?('watched_by?')
25 25 watched = object.watched_by?(user)
26 26 url = {:controller => 'watchers',
27 :action => (watched ? 'remove' : 'add'),
27 :action => (watched ? 'unwatch' : 'watch'),
28 28 :object_type => object.class.to_s.underscore,
29 29 :object_id => object.id}
30 30 link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
@@ -33,4 +33,9 module WatchersHelper
33 33 :class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
34 34
35 35 end
36
37 # Returns a comma separated list of users watching the given object
38 def watchers_list(object)
39 object.watcher_users.collect {|u| content_tag('span', link_to_user(u), :class => 'user') }.join(",\n")
40 end
36 41 end
@@ -19,5 +19,12 class Watcher < ActiveRecord::Base
19 19 belongs_to :watchable, :polymorphic => true
20 20 belongs_to :user
21 21
22 validates_presence_of :user
22 23 validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id]
24
25 protected
26
27 def validate
28 errors.add :user_id, :activerecord_error_invalid unless user.nil? || user.active?
29 end
23 30 end
@@ -78,6 +78,14 end %>
78 78 </div>
79 79 <% end %>
80 80
81 <% if User.current.allowed_to?(:add_issue_watchers, @project) ||
82 (@issue.watchers.any? && User.current.allowed_to?(:view_issue_watchers, @project)) %>
83 <hr />
84 <div id="watchers">
85 <%= render :partial => 'watchers/watchers', :locals => {:watched => @issue} %>
86 </div>
87 <% end %>
88
81 89 </div>
82 90
83 91 <% if @issue.changesets.any? && User.current.allowed_to?(:view_changesets, @project) %>
@@ -633,3 +633,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
633 633 setting_mail_handler_api_key: API key
634 634 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
635 635 field_parent_title: Parent page
636 label_issue_watchers: Watchers
@@ -638,3 +638,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
638 638 setting_mail_handler_api_key: API key
639 639 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
640 640 field_parent_title: Parent page
641 label_issue_watchers: Watchers
@@ -635,3 +635,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
635 635 setting_mail_handler_api_key: API key
636 636 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
637 637 field_parent_title: Parent page
638 label_issue_watchers: Watchers
@@ -634,3 +634,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
634 634 setting_mail_handler_api_key: API key
635 635 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
636 636 field_parent_title: Parent page
637 label_issue_watchers: Watchers
@@ -521,6 +521,7 label_reverse_chronological_order: In reverse chronological order
521 521 label_planning: Planning
522 522 label_incoming_emails: Incoming emails
523 523 label_generate_key: Generate a key
524 label_issue_watchers: Watchers
524 525
525 526 button_login: Login
526 527 button_submit: Submit
@@ -636,3 +636,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
636 636 setting_mail_handler_api_key: API key
637 637 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
638 638 field_parent_title: Parent page
639 label_issue_watchers: Watchers
@@ -633,3 +633,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
633 633 setting_mail_handler_api_key: API key
634 634 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
635 635 field_parent_title: Parent page
636 label_issue_watchers: Watchers
@@ -521,6 +521,7 label_reverse_chronological_order: Dans l'ordre chronologique inverse
521 521 label_planning: Planning
522 522 label_incoming_emails: Emails entrants
523 523 label_generate_key: Générer une clé
524 label_issue_watchers: Utilisateurs surveillant cette demande
524 525
525 526 button_login: Connexion
526 527 button_submit: Soumettre
@@ -633,3 +633,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
633 633 setting_mail_handler_api_key: API key
634 634 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
635 635 field_parent_title: Parent page
636 label_issue_watchers: Watchers
@@ -634,3 +634,4 setting_mail_handler_api_enabled: Web Service engedélyezése a beérkezett leve
634 634 setting_mail_handler_api_key: API kulcs
635 635 text_email_delivery_not_configured: "Az E-mail küldés nincs konfigurálva, és az értesítések ki vannak kapcsolva.\nÁllítsd be az SMTP szervert a config/email.yml fájlban és indítsd újra az alkalmazást, hogy érvénybe lépjen."
636 636 field_parent_title: Parent page
637 label_issue_watchers: Watchers
@@ -633,3 +633,4 setting_mail_handler_api_enabled: Abilita WS per le e-mail in arrivo
633 633 setting_mail_handler_api_key: chiave API
634 634 text_email_delivery_not_configured: "La consegna via e-mail non è configurata e le notifiche sono disabilitate.\nConfigura il tuo server SMTP in config/email.yml e riavvia l'applicazione per abilitarle."
635 635 field_parent_title: Parent page
636 label_issue_watchers: Watchers
@@ -634,3 +634,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
634 634 setting_mail_handler_api_key: API key
635 635 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
636 636 field_parent_title: Parent page
637 label_issue_watchers: Watchers
@@ -633,3 +633,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
633 633 setting_mail_handler_api_key: API key
634 634 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
635 635 field_parent_title: Parent page
636 label_issue_watchers: Watchers
@@ -636,3 +636,4 setting_mail_handler_api_key: API raktas
636 636
637 637 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
638 638 field_parent_title: Parent page
639 label_issue_watchers: Watchers
@@ -634,3 +634,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
634 634 setting_mail_handler_api_key: API key
635 635 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
636 636 field_parent_title: Parent page
637 label_issue_watchers: Watchers
@@ -634,3 +634,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
634 634 setting_mail_handler_api_key: API key
635 635 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
636 636 field_parent_title: Parent page
637 label_issue_watchers: Watchers
@@ -633,3 +633,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
633 633 setting_mail_handler_api_key: API key
634 634 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
635 635 field_parent_title: Parent page
636 label_issue_watchers: Watchers
@@ -633,3 +633,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
633 633 setting_mail_handler_api_key: API key
634 634 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
635 635 field_parent_title: Parent page
636 label_issue_watchers: Watchers
@@ -633,3 +633,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
633 633 setting_mail_handler_api_key: API key
634 634 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
635 635 field_parent_title: Parent page
636 label_issue_watchers: Watchers
@@ -633,3 +633,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
633 633 setting_mail_handler_api_key: API key
634 634 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
635 635 field_parent_title: Parent page
636 label_issue_watchers: Watchers
@@ -637,3 +637,4 setting_mail_handler_api_enabled: Включить веб-сервис для в
637 637 setting_mail_handler_api_key: API ключ
638 638 text_email_delivery_not_configured: "Параметры работы с почтовым сервером не настроены и функция уведомления по email не активна.\nНастроить параметры для вашего SMTP сервера вы можете в файле config/email.yml. Для применения изменений перезапустите приложение."
639 639 field_parent_title: Parent page
640 label_issue_watchers: Watchers
@@ -634,3 +634,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
634 634 setting_mail_handler_api_key: API key
635 635 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
636 636 field_parent_title: Parent page
637 label_issue_watchers: Watchers
@@ -634,3 +634,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
634 634 setting_mail_handler_api_key: API key
635 635 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
636 636 field_parent_title: Parent page
637 label_issue_watchers: Watchers
@@ -636,3 +636,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
636 636 setting_mail_handler_api_key: API key
637 637 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
638 638 field_parent_title: Parent page
639 label_issue_watchers: Watchers
@@ -635,3 +635,4 setting_mail_handler_api_enabled: Enable WS for incoming emails
635 635 setting_mail_handler_api_key: API key
636 636 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
637 637 field_parent_title: Parent page
638 label_issue_watchers: Watchers
@@ -634,3 +634,4 enumeration_issue_priorities: 項目優先權
634 634 enumeration_doc_categories: 文件分類
635 635 enumeration_activities: 活動 (時間追蹤)
636 636 field_parent_title: Parent page
637 label_issue_watchers: Watchers
@@ -634,3 +634,4 enumeration_doc_categories: 文档类别
634 634 enumeration_activities: 活动(时间跟踪)
635 635 text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server in config/email.yml and restart the application to enable them."
636 636 field_parent_title: Parent page
637 label_issue_watchers: Watchers
@@ -46,6 +46,9 Redmine::AccessControl.map do |map|
46 46 # Gantt & calendar
47 47 map.permission :view_gantt, :projects => :gantt
48 48 map.permission :view_calendar, :projects => :calendar
49 # Watchers
50 map.permission :view_issue_watchers, {}
51 map.permission :add_issue_watchers, {:watchers => :new}
49 52 end
50 53
51 54 map.project_module :time_tracking do |map|
@@ -15,6 +15,8 roles_001:
15 15 - :add_issue_notes
16 16 - :move_issues
17 17 - :delete_issues
18 - :view_issue_watchers
19 - :add_issue_watchers
18 20 - :manage_public_queries
19 21 - :save_queries
20 22 - :view_gantt
@@ -58,6 +60,7 roles_002:
58 60 - :add_issue_notes
59 61 - :move_issues
60 62 - :delete_issues
63 - :view_issue_watchers
61 64 - :save_queries
62 65 - :view_gantt
63 66 - :view_calendar
@@ -95,6 +98,7 roles_003:
95 98 - :manage_issue_relations
96 99 - :add_issue_notes
97 100 - :move_issues
101 - :view_issue_watchers
98 102 - :save_queries
99 103 - :view_gantt
100 104 - :view_calendar
@@ -13,6 +13,7 module Redmine
13 13
14 14 class_eval do
15 15 has_many :watchers, :as => :watchable, :dependent => :delete_all
16 has_many :watcher_users, :through => :watchers, :source => :user
16 17 end
17 18 end
18 19 end
@@ -22,25 +23,40 module Redmine
22 23 base.extend ClassMethods
23 24 end
24 25
26 # Returns an array of users that are proposed as watchers
27 def addable_watcher_users
28 self.project.users.sort - self.watcher_users
29 end
30
31 # Adds user as a watcher
25 32 def add_watcher(user)
26 33 self.watchers << Watcher.new(:user => user)
27 34 end
28 35
36 # Removes user from the watchers list
29 37 def remove_watcher(user)
30 38 return nil unless user && user.is_a?(User)
31 39 Watcher.delete_all "watchable_type = '#{self.class}' AND watchable_id = #{self.id} AND user_id = #{user.id}"
32 40 end
33 41
42 # Adds/removes watcher
43 def set_watcher(user, watching=true)
44 watching ? add_watcher(user) : remove_watcher(user)
45 end
46
47 # Returns if object is watched by user
34 48 def watched_by?(user)
35 49 !self.watchers.find(:first,
36 50 :conditions => ["#{Watcher.table_name}.user_id = ?", user.id]).nil?
37 51 end
38 52
53 # Returns an array of watchers' email addresses
39 54 def watcher_recipients
40 55 self.watchers.collect { |w| w.user.mail if w.user.active? }.compact
41 56 end
42 57
43 58 module ClassMethods
59 # Returns the objects that are watched by user
44 60 def watched_by(user)
45 61 find(:all,
46 62 :include => :watchers,
@@ -50,4 +66,4 module Redmine
50 66 end
51 67 end
52 68 end
53 end No newline at end of file
69 end
@@ -138,8 +138,8 module ActionView #:nodoc:
138 138 def add_options(option_tags, options, value = nil)
139 139 option_tags = "<option value=\"\"></option>\n" + option_tags if options[:include_blank]
140 140
141 if value.blank? && options[:prompt]
142 ("<option value=\"\">#{options[:prompt].kind_of?(String) ? options[:prompt] : l(:actionview_instancetag_blank_option)}</option>\n") + option_tags
141 if options[:prompt]
142 ("<option value=\"\">--- #{options[:prompt].kind_of?(String) ? options[:prompt] : l(:actionview_instancetag_blank_option)} ---</option>\n") + option_tags
143 143 else
144 144 option_tags
145 145 end
General Comments 0
You need to be logged in to leave comments. Login now