git-svn-id: http://redmine.rubyforge.org/svn/trunk@1712 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/0.8.0-RC1
@@ -17,30 +17,38 @@ | |||
class WatchersController < ApplicationController | |||
layout 'base' | |||
before_filter :require_login, :find_project, :check_project_privacy | |||
before_filter :find_project | |||
before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch] | |||
before_filter :authorize, :only => :new | |||
def add | |||
user = User.current | |||
@watched.add_watcher(user) | |||
respond_to do |format| | |||
format.html { redirect_to :back } | |||
format.js { render(:update) {|page| page.replace_html 'watcher', watcher_link(@watched, user)} } | |||
end | |||
rescue RedirectBackError | |||
render :text => 'Watcher added.', :layout => true | |||
verify :method => :post, | |||
:only => [ :watch, :unwatch ], | |||
:render => { :nothing => true, :status => :method_not_allowed } | |||
def watch | |||
set_watcher(User.current, true) | |||
end | |||
def unwatch | |||
set_watcher(User.current, false) | |||
end | |||
def remove | |||
user = User.current | |||
@watched.remove_watcher(user) | |||
def new | |||
@watcher = Watcher.new(params[:watcher]) | |||
@watcher.watchable = @watched | |||
@watcher.save if request.post? | |||
respond_to do |format| | |||
format.html { redirect_to :back } | |||
format.js { render(:update) {|page| page.replace_html 'watcher', watcher_link(@watched, user)} } | |||
format.js do | |||
render :update do |page| | |||
page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched} | |||
end | |||
end | |||
end | |||
rescue RedirectBackError | |||
render :text => 'Watcher removed.', :layout => true | |||
rescue ::ActionController::RedirectBackError | |||
render :text => 'Watcher added.', :layout => true | |||
end | |||
private | |||
def find_project | |||
klass = Object.const_get(params[:object_type].camelcase) | |||
@@ -50,4 +58,14 @@ private | |||
rescue | |||
render_404 | |||
end | |||
def set_watcher(user, watching) | |||
@watched.set_watcher(user, watching) | |||
respond_to do |format| | |||
format.html { redirect_to :back } | |||
format.js { render(:update) {|page| page.replace_html 'watcher', watcher_link(@watched, user)} } | |||
end | |||
rescue ::ActionController::RedirectBackError | |||
render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true | |||
end | |||
end |
@@ -24,7 +24,7 @@ module WatchersHelper | |||
return '' unless user && user.logged? && object.respond_to?('watched_by?') | |||
watched = object.watched_by?(user) | |||
url = {:controller => 'watchers', | |||
:action => (watched ? 'remove' : 'add'), | |||
:action => (watched ? 'unwatch' : 'watch'), | |||
:object_type => object.class.to_s.underscore, | |||
:object_id => object.id} | |||
link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)), | |||
@@ -33,4 +33,9 @@ module WatchersHelper | |||
:class => (watched ? 'icon icon-fav' : 'icon icon-fav-off')) | |||
end | |||
# Returns a comma separated list of users watching the given object | |||
def watchers_list(object) | |||
object.watcher_users.collect {|u| content_tag('span', link_to_user(u), :class => 'user') }.join(",\n") | |||
end | |||
end |
@@ -19,5 +19,12 @@ class Watcher < ActiveRecord::Base | |||
belongs_to :watchable, :polymorphic => true | |||
belongs_to :user | |||
validates_presence_of :user | |||
validates_uniqueness_of :user_id, :scope => [:watchable_type, :watchable_id] | |||
protected | |||
def validate | |||
errors.add :user_id, :activerecord_error_invalid unless user.nil? || user.active? | |||
end | |||
end |
@@ -78,6 +78,14 @@ end %> | |||
</div> | |||
<% end %> | |||
<% if User.current.allowed_to?(:add_issue_watchers, @project) || | |||
(@issue.watchers.any? && User.current.allowed_to?(:view_issue_watchers, @project)) %> | |||
<hr /> | |||
<div id="watchers"> | |||
<%= render :partial => 'watchers/watchers', :locals => {:watched => @issue} %> | |||
</div> | |||
<% end %> | |||
</div> | |||
<% if @issue.changesets.any? && User.current.allowed_to?(:view_changesets, @project) %> |
@@ -0,0 +1,25 @@ | |||
<div class="contextual"> | |||
<%= link_to_remote l(:button_add), | |||
:url => {:controller => 'watchers', | |||
:action => 'new', | |||
:object_type => watched.class.name.underscore, | |||
:object_id => watched} if User.current.allowed_to?(:add_issue_watchers, @project) %> | |||
</div> | |||
<p><strong><%= l(:label_issue_watchers) %></strong></p> | |||
<%= watchers_list(watched) %> | |||
<% unless @watcher.nil? %> | |||
<% remote_form_for(:watcher, @watcher, | |||
:url => {:controller => 'watchers', | |||
:action => 'new', | |||
:object_type => watched.class.name.underscore, | |||
:object_id => watched}, | |||
:method => :post, | |||
:html => {:id => 'new-watcher-form'}) do |f| %> | |||
<p><%= f.select :user_id, (watched.addable_watcher_users.collect {|m| [m.name, m.id]}), :prompt => true %> | |||
<%= submit_tag l(:button_add) %> | |||
<%= toggle_link l(:button_cancel), 'new-watcher-form'%></p> | |||
<% end %> | |||
<% end %> |
@@ -633,3 +633,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -638,3 +638,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -635,3 +635,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -634,3 +634,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -521,6 +521,7 @@ label_reverse_chronological_order: In reverse chronological order | |||
label_planning: Planning | |||
label_incoming_emails: Incoming emails | |||
label_generate_key: Generate a key | |||
label_issue_watchers: Watchers | |||
button_login: Login | |||
button_submit: Submit |
@@ -636,3 +636,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -633,3 +633,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -521,6 +521,7 @@ label_reverse_chronological_order: Dans l'ordre chronologique inverse | |||
label_planning: Planning | |||
label_incoming_emails: Emails entrants | |||
label_generate_key: Générer une clé | |||
label_issue_watchers: Utilisateurs surveillant cette demande | |||
button_login: Connexion | |||
button_submit: Soumettre |
@@ -633,3 +633,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -634,3 +634,4 @@ setting_mail_handler_api_enabled: Web Service engedélyezése a beérkezett leve | |||
setting_mail_handler_api_key: API kulcs | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -633,3 +633,4 @@ setting_mail_handler_api_enabled: Abilita WS per le e-mail in arrivo | |||
setting_mail_handler_api_key: chiave API | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -634,3 +634,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -633,3 +633,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -636,3 +636,4 @@ setting_mail_handler_api_key: API raktas | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -634,3 +634,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -634,3 +634,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -633,3 +633,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -633,3 +633,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -633,3 +633,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -633,3 +633,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -637,3 +637,4 @@ setting_mail_handler_api_enabled: Включить веб-сервис для в | |||
setting_mail_handler_api_key: API ключ | |||
text_email_delivery_not_configured: "Параметры работы с почтовым сервером не настроены и функция уведомления по email не активна.\nНастроить параметры для вашего SMTP сервера вы можете в файле config/email.yml. Для применения изменений перезапустите приложение." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -634,3 +634,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -634,3 +634,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -636,3 +636,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -635,3 +635,4 @@ setting_mail_handler_api_enabled: Enable WS for incoming emails | |||
setting_mail_handler_api_key: API key | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -634,3 +634,4 @@ enumeration_issue_priorities: 項目優先權 | |||
enumeration_doc_categories: 文件分類 | |||
enumeration_activities: 活動 (時間追蹤) | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -634,3 +634,4 @@ enumeration_doc_categories: 文档类别 | |||
enumeration_activities: 活动(时间跟踪) | |||
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." | |||
field_parent_title: Parent page | |||
label_issue_watchers: Watchers |
@@ -46,6 +46,9 @@ Redmine::AccessControl.map do |map| | |||
# Gantt & calendar | |||
map.permission :view_gantt, :projects => :gantt | |||
map.permission :view_calendar, :projects => :calendar | |||
# Watchers | |||
map.permission :view_issue_watchers, {} | |||
map.permission :add_issue_watchers, {:watchers => :new} | |||
end | |||
map.project_module :time_tracking do |map| |
@@ -15,6 +15,8 @@ roles_001: | |||
- :add_issue_notes | |||
- :move_issues | |||
- :delete_issues | |||
- :view_issue_watchers | |||
- :add_issue_watchers | |||
- :manage_public_queries | |||
- :save_queries | |||
- :view_gantt | |||
@@ -58,6 +60,7 @@ roles_002: | |||
- :add_issue_notes | |||
- :move_issues | |||
- :delete_issues | |||
- :view_issue_watchers | |||
- :save_queries | |||
- :view_gantt | |||
- :view_calendar | |||
@@ -95,6 +98,7 @@ roles_003: | |||
- :manage_issue_relations | |||
- :add_issue_notes | |||
- :move_issues | |||
- :view_issue_watchers | |||
- :save_queries | |||
- :view_gantt | |||
- :view_calendar |
@@ -0,0 +1,6 @@ | |||
--- | |||
watchers_001: | |||
watchable_type: Issue | |||
watchable_id: 2 | |||
user_id: 3 | |||
@@ -0,0 +1,70 @@ | |||
# Redmine - project management software | |||
# Copyright (C) 2006-2008 Jean-Philippe Lang | |||
# | |||
# 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. | |||
require File.dirname(__FILE__) + '/../test_helper' | |||
require 'watchers_controller' | |||
# Re-raise errors caught by the controller. | |||
class WatchersController; def rescue_action(e) raise e end; end | |||
class WatchersControllerTest < Test::Unit::TestCase | |||
fixtures :projects, :users, :roles, :members, :enabled_modules, | |||
:issues, :trackers, :projects_trackers, :issue_statuses, :enumerations, :watchers | |||
def setup | |||
@controller = WatchersController.new | |||
@request = ActionController::TestRequest.new | |||
@response = ActionController::TestResponse.new | |||
User.current = nil | |||
end | |||
def test_get_watch_should_be_invalid | |||
@request.session[:user_id] = 3 | |||
get :watch, :object_type => 'issue', :object_id => '1' | |||
assert_response 405 | |||
end | |||
def test_watch | |||
@request.session[:user_id] = 3 | |||
assert_difference('Watcher.count') do | |||
xhr :post, :watch, :object_type => 'issue', :object_id => '1' | |||
assert_response :success | |||
assert_select_rjs :replace_html, 'watcher' | |||
end | |||
assert Issue.find(1).watched_by?(User.find(3)) | |||
end | |||
def test_unwatch | |||
@request.session[:user_id] = 3 | |||
assert_difference('Watcher.count', -1) do | |||
xhr :post, :unwatch, :object_type => 'issue', :object_id => '2' | |||
assert_response :success | |||
assert_select_rjs :replace_html, 'watcher' | |||
end | |||
assert !Issue.find(1).watched_by?(User.find(3)) | |||
end | |||
def test_new_watcher | |||
@request.session[:user_id] = 2 | |||
assert_difference('Watcher.count') do | |||
xhr :post, :new, :object_type => 'issue', :object_id => '2', :watcher => {:user_id => '4'} | |||
assert_response :success | |||
assert_select_rjs :replace_html, 'watchers' | |||
end | |||
assert Issue.find(2).watched_by?(User.find(4)) | |||
end | |||
end |
@@ -13,6 +13,7 @@ module Redmine | |||
class_eval do | |||
has_many :watchers, :as => :watchable, :dependent => :delete_all | |||
has_many :watcher_users, :through => :watchers, :source => :user | |||
end | |||
end | |||
end | |||
@@ -22,25 +23,40 @@ module Redmine | |||
base.extend ClassMethods | |||
end | |||
# Returns an array of users that are proposed as watchers | |||
def addable_watcher_users | |||
self.project.users.sort - self.watcher_users | |||
end | |||
# Adds user as a watcher | |||
def add_watcher(user) | |||
self.watchers << Watcher.new(:user => user) | |||
end | |||
# Removes user from the watchers list | |||
def remove_watcher(user) | |||
return nil unless user && user.is_a?(User) | |||
Watcher.delete_all "watchable_type = '#{self.class}' AND watchable_id = #{self.id} AND user_id = #{user.id}" | |||
end | |||
# Adds/removes watcher | |||
def set_watcher(user, watching=true) | |||
watching ? add_watcher(user) : remove_watcher(user) | |||
end | |||
# Returns if object is watched by user | |||
def watched_by?(user) | |||
!self.watchers.find(:first, | |||
:conditions => ["#{Watcher.table_name}.user_id = ?", user.id]).nil? | |||
end | |||
# Returns an array of watchers' email addresses | |||
def watcher_recipients | |||
self.watchers.collect { |w| w.user.mail if w.user.active? }.compact | |||
end | |||
module ClassMethods | |||
# Returns the objects that are watched by user | |||
def watched_by(user) | |||
find(:all, | |||
:include => :watchers, | |||
@@ -50,4 +66,4 @@ module Redmine | |||
end | |||
end | |||
end | |||
end | |||
end |
@@ -138,8 +138,8 @@ module ActionView #:nodoc: | |||
def add_options(option_tags, options, value = nil) | |||
option_tags = "<option value=\"\"></option>\n" + option_tags if options[:include_blank] | |||
if value.blank? && options[:prompt] | |||
("<option value=\"\">#{options[:prompt].kind_of?(String) ? options[:prompt] : l(:actionview_instancetag_blank_option)}</option>\n") + option_tags | |||
if options[:prompt] | |||
("<option value=\"\">--- #{options[:prompt].kind_of?(String) ? options[:prompt] : l(:actionview_instancetag_blank_option)} ---</option>\n") + option_tags | |||
else | |||
option_tags | |||
end |