##// END OF EJS Templates
Cleaner way to handle the replacement of watch links (#8071)....
Jean-Philippe Lang -
r5200:e3dae9ddbf46
parent child
Show More
@@ -1,101 +1,93
1 # redMine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class WatchersController < ApplicationController
18 class WatchersController < ApplicationController
19 before_filter :find_project
19 before_filter :find_project
20 before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
20 before_filter :require_login, :check_project_privacy, :only => [:watch, :unwatch]
21 before_filter :authorize, :only => [:new, :destroy]
21 before_filter :authorize, :only => [:new, :destroy]
22
22
23 verify :method => :post,
23 verify :method => :post,
24 :only => [ :watch, :unwatch ],
24 :only => [ :watch, :unwatch ],
25 :render => { :nothing => true, :status => :method_not_allowed }
25 :render => { :nothing => true, :status => :method_not_allowed }
26
26
27 def watch
27 def watch
28 if @watched.respond_to?(:visible?) && !@watched.visible?(User.current)
28 if @watched.respond_to?(:visible?) && !@watched.visible?(User.current)
29 render_403
29 render_403
30 else
30 else
31 set_watcher(User.current, true)
31 set_watcher(User.current, true)
32 end
32 end
33 end
33 end
34
34
35 def unwatch
35 def unwatch
36 set_watcher(User.current, false)
36 set_watcher(User.current, false)
37 end
37 end
38
38
39 def new
39 def new
40 @watcher = Watcher.new(params[:watcher])
40 @watcher = Watcher.new(params[:watcher])
41 @watcher.watchable = @watched
41 @watcher.watchable = @watched
42 @watcher.save if request.post?
42 @watcher.save if request.post?
43 respond_to do |format|
43 respond_to do |format|
44 format.html { redirect_to :back }
44 format.html { redirect_to :back }
45 format.js do
45 format.js do
46 render :update do |page|
46 render :update do |page|
47 page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
47 page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
48 end
48 end
49 end
49 end
50 end
50 end
51 rescue ::ActionController::RedirectBackError
51 rescue ::ActionController::RedirectBackError
52 render :text => 'Watcher added.', :layout => true
52 render :text => 'Watcher added.', :layout => true
53 end
53 end
54
54
55 def destroy
55 def destroy
56 @watched.set_watcher(User.find(params[:user_id]), false) if request.post?
56 @watched.set_watcher(User.find(params[:user_id]), false) if request.post?
57 respond_to do |format|
57 respond_to do |format|
58 format.html { redirect_to :back }
58 format.html { redirect_to :back }
59 format.js do
59 format.js do
60 render :update do |page|
60 render :update do |page|
61 page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
61 page.replace_html 'watchers', :partial => 'watchers/watchers', :locals => {:watched => @watched}
62 end
62 end
63 end
63 end
64 end
64 end
65 end
65 end
66
66
67 private
67 private
68 def find_project
68 def find_project
69 klass = Object.const_get(params[:object_type].camelcase)
69 klass = Object.const_get(params[:object_type].camelcase)
70 return false unless klass.respond_to?('watched_by')
70 return false unless klass.respond_to?('watched_by')
71 @watched = klass.find(params[:object_id])
71 @watched = klass.find(params[:object_id])
72 @project = @watched.project
72 @project = @watched.project
73 rescue
73 rescue
74 render_404
74 render_404
75 end
75 end
76
76
77 def set_watcher(user, watching)
77 def set_watcher(user, watching)
78 @watched.set_watcher(user, watching)
78 @watched.set_watcher(user, watching)
79 if params[:replace].present?
80 if params[:replace].is_a? Array
81 replace_ids = params[:replace]
82 else
83 replace_ids = [params[:replace]]
84 end
85 else
86 replace_ids = ['watcher']
87 end
88 respond_to do |format|
79 respond_to do |format|
89 format.html { redirect_to :back }
80 format.html { redirect_to :back }
90 format.js do
81 format.js do
91 render(:update) do |page|
82 render(:update) do |page|
92 replace_ids.each do |replace_id|
83 c = watcher_css(@watched)
93 page.replace_html replace_id, watcher_link(@watched, user, :replace => replace_ids)
84 page.select(".#{c}").each do |item|
85 page.replace_html item, watcher_link(@watched, user)
94 end
86 end
95 end
87 end
96 end
88 end
97 end
89 end
98 rescue ::ActionController::RedirectBackError
90 rescue ::ActionController::RedirectBackError
99 render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true
91 render :text => (watching ? 'Watcher added.' : 'Watcher removed.'), :layout => true
100 end
92 end
101 end
93 end
@@ -1,69 +1,64
1 # redMine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 module WatchersHelper
18 module WatchersHelper
19
19
20 # Valid options
20 def watcher_tag(object, user, options={})
21 # * :id - the element id
21 content_tag("span", watcher_link(object, user), :class => watcher_css(object))
22 # * :replace - a string or array of element ids that will be
23 # replaced
24 def watcher_tag(object, user, options={:replace => 'watcher'})
25 id = options[:id]
26 id ||= options[:replace] if options[:replace].is_a? String
27 content_tag("span", watcher_link(object, user, options), :id => id)
28 end
22 end
29
23
30 # Valid options
24 def watcher_link(object, user)
31 # * :replace - a string or array of element ids that will be
32 # replaced
33 def watcher_link(object, user, options={:replace => 'watcher'})
34 return '' unless user && user.logged? && object.respond_to?('watched_by?')
25 return '' unless user && user.logged? && object.respond_to?('watched_by?')
35 watched = object.watched_by?(user)
26 watched = object.watched_by?(user)
36 url = {:controller => 'watchers',
27 url = {:controller => 'watchers',
37 :action => (watched ? 'unwatch' : 'watch'),
28 :action => (watched ? 'unwatch' : 'watch'),
38 :object_type => object.class.to_s.underscore,
29 :object_type => object.class.to_s.underscore,
39 :object_id => object.id,
30 :object_id => object.id}
40 :replace => options[:replace]}
41 link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
31 link_to_remote((watched ? l(:button_unwatch) : l(:button_watch)),
42 {:url => url},
32 {:url => url},
43 :href => url_for(url),
33 :href => url_for(url),
44 :class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
34 :class => (watched ? 'icon icon-fav' : 'icon icon-fav-off'))
45
35
46 end
36 end
47
37
38 # Returns the css class used to identify watch links for a given +object+
39 def watcher_css(object)
40 "#{object.class.to_s.underscore}-#{object.id}-watcher"
41 end
42
48 # Returns a comma separated list of users watching the given object
43 # Returns a comma separated list of users watching the given object
49 def watchers_list(object)
44 def watchers_list(object)
50 remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
45 remove_allowed = User.current.allowed_to?("delete_#{object.class.name.underscore}_watchers".to_sym, object.project)
51 lis = object.watcher_users.collect do |user|
46 lis = object.watcher_users.collect do |user|
52 s = avatar(user, :size => "16").to_s + link_to_user(user, :class => 'user').to_s
47 s = avatar(user, :size => "16").to_s + link_to_user(user, :class => 'user').to_s
53 if remove_allowed
48 if remove_allowed
54 url = {:controller => 'watchers',
49 url = {:controller => 'watchers',
55 :action => 'destroy',
50 :action => 'destroy',
56 :object_type => object.class.to_s.underscore,
51 :object_type => object.class.to_s.underscore,
57 :object_id => object.id,
52 :object_id => object.id,
58 :user_id => user}
53 :user_id => user}
59 s += ' ' + link_to_remote(image_tag('delete.png'),
54 s += ' ' + link_to_remote(image_tag('delete.png'),
60 {:url => url},
55 {:url => url},
61 :href => url_for(url),
56 :href => url_for(url),
62 :style => "vertical-align: middle",
57 :style => "vertical-align: middle",
63 :class => "delete")
58 :class => "delete")
64 end
59 end
65 "<li>#{ s }</li>"
60 "<li>#{ s }</li>"
66 end
61 end
67 lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>"
62 lis.empty? ? "" : "<ul>#{ lis.join("\n") }</ul>"
68 end
63 end
69 end
64 end
@@ -1,10 +1,9
1 <div class="contextual">
1 <div class="contextual">
2 <%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
2 <%= link_to_if_authorized(l(:button_update), {:controller => 'issues', :action => 'edit', :id => @issue }, :onclick => 'showAndScrollTo("update", "notes"); return false;', :class => 'icon icon-edit', :accesskey => accesskey(:edit)) %>
3 <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, :class => 'icon icon-time-add' %>
3 <%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'new', :issue_id => @issue}, :class => 'icon icon-time-add' %>
4 <% replace_watcher ||= 'watcher' %>
4 <%= watcher_tag(@issue, User.current) %>
5 <%= watcher_tag(@issue, User.current, {:id => replace_watcher, :replace => ['watcher','watcher2']}) %>
6 <%= link_to_if_authorized l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-duplicate' %>
5 <%= link_to_if_authorized l(:button_duplicate), {:controller => 'issues', :action => 'new', :project_id => @project, :copy_from => @issue }, :class => 'icon icon-duplicate' %>
7 <%= link_to_if_authorized l(:button_copy), {:controller => 'issue_moves', :action => 'new', :id => @issue, :copy_options => {:copy => 't'}}, :class => 'icon icon-copy' %>
6 <%= link_to_if_authorized l(:button_copy), {:controller => 'issue_moves', :action => 'new', :id => @issue, :copy_options => {:copy => 't'}}, :class => 'icon icon-copy' %>
8 <%= link_to_if_authorized l(:button_move), {:controller => 'issue_moves', :action => 'new', :id => @issue}, :class => 'icon icon-move' %>
7 <%= link_to_if_authorized l(:button_move), {:controller => 'issue_moves', :action => 'new', :id => @issue}, :class => 'icon icon-move' %>
9 <%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => (@issue.leaf? ? l(:text_are_you_sure) : l(:text_are_you_sure_with_children)), :method => :post, :class => 'icon icon-del' %>
8 <%= link_to_if_authorized l(:button_delete), {:controller => 'issues', :action => 'destroy', :id => @issue}, :confirm => (@issue.leaf? ? l(:text_are_you_sure) : l(:text_are_you_sure_with_children)), :method => :post, :class => 'icon icon-del' %>
10 </div>
9 </div>
@@ -1,137 +1,137
1 <%= render :partial => 'action_menu' %>
1 <%= render :partial => 'action_menu' %>
2
2
3 <h2><%= @issue.tracker.name %> #<%= @issue.id %></h2>
3 <h2><%= @issue.tracker.name %> #<%= @issue.id %></h2>
4
4
5 <div class="<%= @issue.css_classes %> details">
5 <div class="<%= @issue.css_classes %> details">
6 <%= avatar(@issue.author, :size => "50") %>
6 <%= avatar(@issue.author, :size => "50") %>
7
7
8 <div class="subject">
8 <div class="subject">
9 <%= render_issue_subject_with_tree(@issue) %>
9 <%= render_issue_subject_with_tree(@issue) %>
10 </div>
10 </div>
11 <p class="author">
11 <p class="author">
12 <%= authoring @issue.created_on, @issue.author %>.
12 <%= authoring @issue.created_on, @issue.author %>.
13 <% if @issue.created_on != @issue.updated_on %>
13 <% if @issue.created_on != @issue.updated_on %>
14 <%= l(:label_updated_time, time_tag(@issue.updated_on)) %>.
14 <%= l(:label_updated_time, time_tag(@issue.updated_on)) %>.
15 <% end %>
15 <% end %>
16 </p>
16 </p>
17
17
18 <table class="attributes">
18 <table class="attributes">
19 <tr>
19 <tr>
20 <th class="status"><%=l(:field_status)%>:</th><td class="status"><%= @issue.status.name %></td>
20 <th class="status"><%=l(:field_status)%>:</th><td class="status"><%= @issue.status.name %></td>
21 <th class="start-date"><%=l(:field_start_date)%>:</th><td class="start-date"><%= format_date(@issue.start_date) %></td>
21 <th class="start-date"><%=l(:field_start_date)%>:</th><td class="start-date"><%= format_date(@issue.start_date) %></td>
22 </tr>
22 </tr>
23 <tr>
23 <tr>
24 <th class="priority"><%=l(:field_priority)%>:</th><td class="priority"><%= @issue.priority.name %></td>
24 <th class="priority"><%=l(:field_priority)%>:</th><td class="priority"><%= @issue.priority.name %></td>
25 <th class="due-date"><%=l(:field_due_date)%>:</th><td class="due-date"><%= format_date(@issue.due_date) %></td>
25 <th class="due-date"><%=l(:field_due_date)%>:</th><td class="due-date"><%= format_date(@issue.due_date) %></td>
26 </tr>
26 </tr>
27 <tr>
27 <tr>
28 <th class="assigned-to"><%=l(:field_assigned_to)%>:</th><td class="assigned-to"><%= avatar(@issue.assigned_to, :size => "14") %><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
28 <th class="assigned-to"><%=l(:field_assigned_to)%>:</th><td class="assigned-to"><%= avatar(@issue.assigned_to, :size => "14") %><%= @issue.assigned_to ? link_to_user(@issue.assigned_to) : "-" %></td>
29 <th class="progress"><%=l(:field_done_ratio)%>:</th><td class="progress"><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
29 <th class="progress"><%=l(:field_done_ratio)%>:</th><td class="progress"><%= progress_bar @issue.done_ratio, :width => '80px', :legend => "#{@issue.done_ratio}%" %></td>
30 </tr>
30 </tr>
31 <tr>
31 <tr>
32 <th class="category"><%=l(:field_category)%>:</th><td class="category"><%=h @issue.category ? @issue.category.name : "-" %></td>
32 <th class="category"><%=l(:field_category)%>:</th><td class="category"><%=h @issue.category ? @issue.category.name : "-" %></td>
33 <% if User.current.allowed_to?(:view_time_entries, @project) %>
33 <% if User.current.allowed_to?(:view_time_entries, @project) %>
34 <th class="spent-time"><%=l(:label_spent_time)%>:</th>
34 <th class="spent-time"><%=l(:label_spent_time)%>:</th>
35 <td class="spent-time"><%= @issue.spent_hours > 0 ? (link_to l_hours(@issue.spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td>
35 <td class="spent-time"><%= @issue.spent_hours > 0 ? (link_to l_hours(@issue.spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td>
36 <% end %>
36 <% end %>
37 </tr>
37 </tr>
38 <tr>
38 <tr>
39 <th class="fixed-version"><%=l(:field_fixed_version)%>:</th><td class="fixed-version"><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
39 <th class="fixed-version"><%=l(:field_fixed_version)%>:</th><td class="fixed-version"><%= @issue.fixed_version ? link_to_version(@issue.fixed_version) : "-" %></td>
40 <% if @issue.estimated_hours %>
40 <% if @issue.estimated_hours %>
41 <th class="estimated-hours"><%=l(:field_estimated_hours)%>:</th><td class="estimated-hours"><%= l_hours(@issue.estimated_hours) %></td>
41 <th class="estimated-hours"><%=l(:field_estimated_hours)%>:</th><td class="estimated-hours"><%= l_hours(@issue.estimated_hours) %></td>
42 <% end %>
42 <% end %>
43 </tr>
43 </tr>
44 <%= render_custom_fields_rows(@issue) %>
44 <%= render_custom_fields_rows(@issue) %>
45 <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
45 <%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
46 </table>
46 </table>
47
47
48 <% if @issue.description? || @issue.attachments.any? -%>
48 <% if @issue.description? || @issue.attachments.any? -%>
49 <hr />
49 <hr />
50 <% if @issue.description? %>
50 <% if @issue.description? %>
51 <div class="contextual">
51 <div class="contextual">
52 <%= link_to_remote_if_authorized(l(:button_quote), { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, :class => 'icon icon-comment') %>
52 <%= link_to_remote_if_authorized(l(:button_quote), { :url => {:controller => 'journals', :action => 'new', :id => @issue} }, :class => 'icon icon-comment') %>
53 </div>
53 </div>
54
54
55 <p><strong><%=l(:field_description)%></strong></p>
55 <p><strong><%=l(:field_description)%></strong></p>
56 <div class="wiki">
56 <div class="wiki">
57 <%= textilizable @issue, :description, :attachments => @issue.attachments %>
57 <%= textilizable @issue, :description, :attachments => @issue.attachments %>
58 </div>
58 </div>
59 <% end %>
59 <% end %>
60 <%= link_to_attachments @issue %>
60 <%= link_to_attachments @issue %>
61 <% end -%>
61 <% end -%>
62
62
63 <%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
63 <%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
64
64
65 <% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %>
65 <% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %>
66 <hr />
66 <hr />
67 <div id="issue_tree">
67 <div id="issue_tree">
68 <div class="contextual">
68 <div class="contextual">
69 <%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %>
69 <%= link_to(l(:button_add), {:controller => 'issues', :action => 'new', :project_id => @project, :issue => {:parent_issue_id => @issue}}) if User.current.allowed_to?(:manage_subtasks, @project) %>
70 </div>
70 </div>
71 <p><strong><%=l(:label_subtask_plural)%></strong></p>
71 <p><strong><%=l(:label_subtask_plural)%></strong></p>
72 <%= render_descendants_tree(@issue) unless @issue.leaf? %>
72 <%= render_descendants_tree(@issue) unless @issue.leaf? %>
73 </div>
73 </div>
74 <% end %>
74 <% end %>
75
75
76 <% if authorize_for('issue_relations', 'new') || @issue.relations.present? %>
76 <% if authorize_for('issue_relations', 'new') || @issue.relations.present? %>
77 <hr />
77 <hr />
78 <div id="relations">
78 <div id="relations">
79 <%= render :partial => 'relations' %>
79 <%= render :partial => 'relations' %>
80 </div>
80 </div>
81 <% end %>
81 <% end %>
82
82
83 </div>
83 </div>
84
84
85 <% if @changesets.present? %>
85 <% if @changesets.present? %>
86 <div id="issue-changesets">
86 <div id="issue-changesets">
87 <h3><%=l(:label_associated_revisions)%></h3>
87 <h3><%=l(:label_associated_revisions)%></h3>
88 <%= render :partial => 'changesets', :locals => { :changesets => @changesets} %>
88 <%= render :partial => 'changesets', :locals => { :changesets => @changesets} %>
89 </div>
89 </div>
90 <% end %>
90 <% end %>
91
91
92 <% if @journals.present? %>
92 <% if @journals.present? %>
93 <div id="history">
93 <div id="history">
94 <h3><%=l(:label_history)%></h3>
94 <h3><%=l(:label_history)%></h3>
95 <%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %>
95 <%= render :partial => 'history', :locals => { :issue => @issue, :journals => @journals } %>
96 </div>
96 </div>
97 <% end %>
97 <% end %>
98
98
99
99
100 <div style="clear: both;"></div>
100 <div style="clear: both;"></div>
101 <%= render :partial => 'action_menu', :locals => {:replace_watcher => 'watcher2' } %>
101 <%= render :partial => 'action_menu' %>
102
102
103 <div style="clear: both;"></div>
103 <div style="clear: both;"></div>
104 <% if authorize_for('issues', 'edit') %>
104 <% if authorize_for('issues', 'edit') %>
105 <div id="update" style="display:none;">
105 <div id="update" style="display:none;">
106 <h3><%= l(:button_update) %></h3>
106 <h3><%= l(:button_update) %></h3>
107 <%= render :partial => 'edit' %>
107 <%= render :partial => 'edit' %>
108 </div>
108 </div>
109 <% end %>
109 <% end %>
110
110
111 <% other_formats_links do |f| %>
111 <% other_formats_links do |f| %>
112 <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
112 <%= f.link_to 'Atom', :url => {:key => User.current.rss_key} %>
113 <%= f.link_to 'PDF' %>
113 <%= f.link_to 'PDF' %>
114 <% end %>
114 <% end %>
115
115
116 <% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>
116 <% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>
117
117
118 <% content_for :sidebar do %>
118 <% content_for :sidebar do %>
119 <%= render :partial => 'issues/sidebar' %>
119 <%= render :partial => 'issues/sidebar' %>
120
120
121 <% if User.current.allowed_to?(:add_issue_watchers, @project) ||
121 <% if User.current.allowed_to?(:add_issue_watchers, @project) ||
122 (@issue.watchers.present? && User.current.allowed_to?(:view_issue_watchers, @project)) %>
122 (@issue.watchers.present? && User.current.allowed_to?(:view_issue_watchers, @project)) %>
123 <div id="watchers">
123 <div id="watchers">
124 <%= render :partial => 'watchers/watchers', :locals => {:watched => @issue} %>
124 <%= render :partial => 'watchers/watchers', :locals => {:watched => @issue} %>
125 </div>
125 </div>
126 <% end %>
126 <% end %>
127 <% end %>
127 <% end %>
128
128
129 <% content_for :header_tags do %>
129 <% content_for :header_tags do %>
130 <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %>
130 <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %>
131 <%= stylesheet_link_tag 'scm' %>
131 <%= stylesheet_link_tag 'scm' %>
132 <%= javascript_include_tag 'context_menu' %>
132 <%= javascript_include_tag 'context_menu' %>
133 <%= stylesheet_link_tag 'context_menu' %>
133 <%= stylesheet_link_tag 'context_menu' %>
134 <%= stylesheet_link_tag 'context_menu_rtl' if l(:direction) == 'rtl' %>
134 <%= stylesheet_link_tag 'context_menu_rtl' if l(:direction) == 'rtl' %>
135 <% end %>
135 <% end %>
136 <div id="context-menu" style="display: none;"></div>
136 <div id="context-menu" style="display: none;"></div>
137 <%= javascript_tag "new ContextMenu('#{issues_context_menu_path}')" %>
137 <%= javascript_tag "new ContextMenu('#{issues_context_menu_path}')" %>
@@ -1,110 +1,89
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2008 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
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
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.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19 require 'watchers_controller'
19 require 'watchers_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class WatchersController; def rescue_action(e) raise e end; end
22 class WatchersController; def rescue_action(e) raise e end; end
23
23
24 class WatchersControllerTest < ActionController::TestCase
24 class WatchersControllerTest < ActionController::TestCase
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules,
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules,
26 :issues, :trackers, :projects_trackers, :issue_statuses, :enumerations, :watchers
26 :issues, :trackers, :projects_trackers, :issue_statuses, :enumerations, :watchers
27
27
28 def setup
28 def setup
29 @controller = WatchersController.new
29 @controller = WatchersController.new
30 @request = ActionController::TestRequest.new
30 @request = ActionController::TestRequest.new
31 @response = ActionController::TestResponse.new
31 @response = ActionController::TestResponse.new
32 User.current = nil
32 User.current = nil
33 end
33 end
34
34
35 def test_get_watch_should_be_invalid
35 def test_get_watch_should_be_invalid
36 @request.session[:user_id] = 3
36 @request.session[:user_id] = 3
37 get :watch, :object_type => 'issue', :object_id => '1'
37 get :watch, :object_type => 'issue', :object_id => '1'
38 assert_response 405
38 assert_response 405
39 end
39 end
40
40
41 def test_watch
41 def test_watch
42 @request.session[:user_id] = 3
42 @request.session[:user_id] = 3
43 assert_difference('Watcher.count') do
43 assert_difference('Watcher.count') do
44 xhr :post, :watch, :object_type => 'issue', :object_id => '1'
44 xhr :post, :watch, :object_type => 'issue', :object_id => '1'
45 assert_response :success
45 assert_response :success
46 assert_select_rjs :replace_html, 'watcher'
46 assert @response.body.include?('$$(".issue-1-watcher")')
47 end
47 end
48 assert Issue.find(1).watched_by?(User.find(3))
48 assert Issue.find(1).watched_by?(User.find(3))
49 end
49 end
50
50
51 def test_watch_should_be_denied_without_permission
51 def test_watch_should_be_denied_without_permission
52 Role.find(2).remove_permission! :view_issues
52 Role.find(2).remove_permission! :view_issues
53 @request.session[:user_id] = 3
53 @request.session[:user_id] = 3
54 assert_no_difference('Watcher.count') do
54 assert_no_difference('Watcher.count') do
55 xhr :post, :watch, :object_type => 'issue', :object_id => '1'
55 xhr :post, :watch, :object_type => 'issue', :object_id => '1'
56 assert_response 403
56 assert_response 403
57 end
57 end
58 end
58 end
59
60 def test_watch_with_multiple_replacements
61 @request.session[:user_id] = 3
62 assert_difference('Watcher.count') do
63 xhr :post, :watch, :object_type => 'issue', :object_id => '1', :replace => ['watch_item_1','watch_item_2']
64 assert_response :success
65 assert_select_rjs :replace_html, 'watch_item_1'
66 assert_select_rjs :replace_html, 'watch_item_2'
67 end
68 end
69
59
70 def test_unwatch
60 def test_unwatch
71 @request.session[:user_id] = 3
61 @request.session[:user_id] = 3
72 assert_difference('Watcher.count', -1) do
62 assert_difference('Watcher.count', -1) do
73 xhr :post, :unwatch, :object_type => 'issue', :object_id => '2'
63 xhr :post, :unwatch, :object_type => 'issue', :object_id => '2'
74 assert_response :success
64 assert_response :success
75 assert_select_rjs :replace_html, 'watcher'
65 assert @response.body.include?('$$(".issue-2-watcher")')
76 end
77 assert !Issue.find(1).watched_by?(User.find(3))
78 end
79
80 def test_unwatch_with_multiple_replacements
81 @request.session[:user_id] = 3
82 assert_difference('Watcher.count', -1) do
83 xhr :post, :unwatch, :object_type => 'issue', :object_id => '2', :replace => ['watch_item_1', 'watch_item_2']
84 assert_response :success
85 assert_select_rjs :replace_html, 'watch_item_1'
86 assert_select_rjs :replace_html, 'watch_item_2'
87 end
66 end
88 assert !Issue.find(1).watched_by?(User.find(3))
67 assert !Issue.find(1).watched_by?(User.find(3))
89 end
68 end
90
69
91 def test_new_watcher
70 def test_new_watcher
92 @request.session[:user_id] = 2
71 @request.session[:user_id] = 2
93 assert_difference('Watcher.count') do
72 assert_difference('Watcher.count') do
94 xhr :post, :new, :object_type => 'issue', :object_id => '2', :watcher => {:user_id => '4'}
73 xhr :post, :new, :object_type => 'issue', :object_id => '2', :watcher => {:user_id => '4'}
95 assert_response :success
74 assert_response :success
96 assert_select_rjs :replace_html, 'watchers'
75 assert_select_rjs :replace_html, 'watchers'
97 end
76 end
98 assert Issue.find(2).watched_by?(User.find(4))
77 assert Issue.find(2).watched_by?(User.find(4))
99 end
78 end
100
79
101 def test_remove_watcher
80 def test_remove_watcher
102 @request.session[:user_id] = 2
81 @request.session[:user_id] = 2
103 assert_difference('Watcher.count', -1) do
82 assert_difference('Watcher.count', -1) do
104 xhr :post, :destroy, :object_type => 'issue', :object_id => '2', :user_id => '3'
83 xhr :post, :destroy, :object_type => 'issue', :object_id => '2', :user_id => '3'
105 assert_response :success
84 assert_response :success
106 assert_select_rjs :replace_html, 'watchers'
85 assert_select_rjs :replace_html, 'watchers'
107 end
86 end
108 assert !Issue.find(2).watched_by?(User.find(3))
87 assert !Issue.find(2).watched_by?(User.find(3))
109 end
88 end
110 end
89 end
General Comments 0
You need to be logged in to leave comments. Login now