Project

General

Profile

Feature #337 » redmine-private_issues.v.0.1.patch

Paul Zubarev, 2009-01-07 22:35

View differences:

redmine-0.8.0/app/controllers/projects_controller.rb 2009-01-07 17:17:01.000000000 +0300
91 91
    
92 92
    cond = @project.project_condition(Setting.display_subprojects_issues?)
93 93
    Issue.visible_by(User.current) do
94
      issue_cond = cond;
95
      if not User.current.allowed_to?(:view_private_issues, @project)
96
        issue_cond += " AND #{Issue.table_name}.private = false"
97
      end
94 98
      @open_issues_by_tracker = Issue.count(:group => :tracker,
95 99
                                            :include => [:project, :status, :tracker],
96
                                            :conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
100
                                            :conditions => ["(#{issue_cond}) AND #{IssueStatus.table_name}.is_closed=?", false])
101
      @private_issues_by_tracker = Issue.count(:group => :tracker,
102
                                               :include => [:project, :status, :tracker],
103
                                               :conditions => ["(#{cond}) AND #{IssueStatus.table_name}.is_closed=?", false],
104
                                               :conditions => ["#{Issue.table_name}.private=?",true])
97 105
      @total_issues_by_tracker = Issue.count(:group => :tracker,
98 106
                                            :include => [:project, :status, :tracker],
99 107
                                            :conditions => cond)
......
240 248
    @activity.scope = (@author.nil? ? :default : :all) if @activity.scope.empty?
241 249

  
242 250
    events = @activity.events(@date_from, @date_to)
243
    
251

  
252
    # The private issues should be removed from events
253
    events.each do |event|
254
      events.delete(event) if event.kind_of?(Issue) && !event.visible?(User.current, Project.find(event.project))
255
    end
256

  
244 257
    respond_to do |format|
245 258
      format.html { 
246 259
        @events_by_day = events.group_by(&:event_date)
redmine-0.8.0/app/models/issue.rb 2009-01-02 02:33:40.000000000 +0300
262 262
      yield
263 263
    end
264 264
  end
265

  
266
  def visible? (usr, project)
267
     private==false || private==true && usr.allowed_to?(:view_private_issues, project)
268
  end
265 269
  
266 270
  def to_s
267 271
    "#{tracker} ##{id}: #{subject}"
redmine-0.8.0/app/views/issues/_form.rhtml 2009-01-02 00:08:51.000000000 +0300
41 41
<p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p>
42 42
</div>
43 43

  
44
<% if User.current.allowed_to?(:add_private_issues, @project) %>
45
<p><%=f.check_box :private %></p>
46
<% end%>
47

  
44 48
<div style="clear:both;"> </div>
45 49
<%= render :partial => 'form_custom_fields' %>
46 50

  
redmine-0.8.0/app/views/issues/_list.rhtml 2009-01-02 02:36:16.000000000 +0300
11 11
	</tr></thead>
12 12
	<tbody>
13 13
	<% issues.each do |issue| -%>
14
	<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= css_issue_classes(issue) %>">
15
	    <td class="checkbox"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
16
		<td><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
17
        <% query.columns.each do |column| %><%= content_tag 'td', column_content(column, issue), :class => column.name %><% end %>
18
	</tr>
14
          <% if issue.visible? User.current, @project %>
15
        	<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= css_issue_classes(issue) %>">
16
              <td class="checkbox"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td>
17
              <td><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td>
18
              <% query.columns.each do |column| %><%= content_tag 'td', column_content(column, issue), :class => column.name %><% end %>
19
            </tr>
20
          <% end %>
19 21
	<% end -%>
20 22
	</tbody>
21 23
</table>
redmine-0.8.0/app/views/issues/_list_simple.rhtml 2009-01-02 03:35:46.000000000 +0300
8 8
		</tr></thead>
9 9
		<tbody>	
10 10
		<% for issue in issues %>
11
		<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= css_issue_classes(issue) %>">
12
			<td class="id">
13
			    <%= check_box_tag("ids[]", issue.id, false, :style => 'display:none;') %>
14
				<%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %>
15
			</td>
16
			<td><%=h issue.project.name %> - <%= issue.tracker.name %><br />
17
                <%= issue.status.name %> - <%= format_time(issue.updated_on) %></td>
18
			<td class="subject">
19
                <%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %>
20
            </td>
21
		</tr>
11
          <% if issue.visible? User.current, @project %>
12
            <tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= css_issue_classes(issue) %>">
13
                <td class="id">
14
                    <%= check_box_tag("ids[]", issue.id, false, :style => 'display:none;') %>
15
                    <%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %>
16
                </td>
17
                <td><%=h issue.project.name %> - <%= issue.tracker.name %><br />
18
                    <%= issue.status.name %> - <%= format_time(issue.updated_on) %></td>
19
                <td class="subject">
20
                    <%= link_to h(issue.subject), :controller => 'issues', :action => 'show', :id => issue %>
21
                </td>
22
            </tr>
23
          <% end %>
22 24
		<% end %>
23 25
		</tbody>
24 26
	</table>
redmine-0.8.0/app/views/issues/show.rhtml 2009-01-02 02:45:22.000000000 +0300
1
<% if @issue.visible? User.current, @project%>
2

  
1 3
<div class="contextual">
2 4
<%= 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 5
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, :class => 'icon icon-time' %>
......
42 46
    <% if @issue.estimated_hours %>
43 47
    <td class="estimated-hours"><b><%=l(:field_estimated_hours)%>:</b></td><td><%= lwr(:label_f_hour, @issue.estimated_hours) %></td>
44 48
    <% end %>
49
    <% if @issue.private %>
50
    <td class="private-issue"><b><%=l(:field_private)%></b></td>
51
    <% end %>
45 52
</tr>
46 53
<tr>
47 54
<% n = 0 -%>
......
126 133
    <%= auto_discovery_link_tag(:atom, {:format => 'atom', :key => User.current.rss_key}, :title => "#{@issue.project} - #{@issue.tracker} ##{@issue.id}: #{@issue.subject}") %>
127 134
    <%= stylesheet_link_tag 'scm' %>
128 135
<% end %>
136

  
137
<% else %>
138
  <p class="nodata"><%=l(:label_access_denied)%> </p>
139
<% end %>
redmine-0.8.0/app/views/projects/show.rhtml 2009-01-02 05:25:04.000000000 +0300
26 26
                                                :set_filter => 1, 
27 27
                                                "tracker_id" => tracker.id %>:
28 28
      <%= @open_issues_by_tracker[tracker] || 0 %> <%= lwr(:label_open_issues, @open_issues_by_tracker[tracker] || 0) %>
29
      (<%= @private_issues_by_tracker[tracker] || 0 %> <%= lwr(:label_private_issues, @private_issues_by_tracker[tracker] || 0)%>)
29 30
      <%= l(:label_on) %> <%= @total_issues_by_tracker[tracker] || 0 %></li>
30 31
    <% end %>
31 32
    </ul>
redmine-0.8.0/db/migrate/102_add_issues_private_flag.rb 2009-01-01 12:47:54.000000000 +0300
1
class AddIssuesPrivateFlag < ActiveRecord::Migration
2
  def self.up
3
    add_column :issues, :private, :boolean, :default => false, :null => false
4
  end
5

  
6
  def self.down
7
    remove_column :issues, :private
8
  end
9
end
redmine-0.8.0/lang/en.yml 2009-01-02 05:36:32.000000000 +0300
184 184
field_default_value: Default value
185 185
field_comments_sorting: Display comments
186 186
field_parent_title: Parent page
187
field_private: Private issue
187 188

  
188 189
setting_app_title: Application title
189 190
setting_app_subtitle: Application subtitle
......
231 232
permission_manage_versions: Manage versions
232 233
permission_manage_categories: Manage issue categories
233 234
permission_add_issues: Add issues
235
permission_add_private_issues: Add private issues
236
permission_view_private_issues: View private issues
234 237
permission_edit_issues: Edit issues
235 238
permission_manage_issue_relations: Manage issue relations
236 239
permission_add_issue_notes: Add notes
......
283 286
project_module_repository: Repository
284 287
project_module_boards: Boards
285 288

  
289
label_access_denied: Access denied
286 290
label_user: User
287 291
label_user_plural: Users
288 292
label_user_new: New user
......
395 399
label_public_projects: Public projects
396 400
label_open_issues: open
397 401
label_open_issues_plural: open
402
label_private_issues: private
403
label_private_issues_plural: private
398 404
label_closed_issues: closed
399 405
label_closed_issues_plural: closed
400 406
label_total: Total
redmine-0.8.0/lang/ru.yml 2009-01-02 05:35:59.000000000 +0300
179 179
field_port: Порт
180 180
field_possible_values: Возможные значения
181 181
field_priority: Приоритет
182
field_private: Конфиденциальная задача
182 183
field_project: Проект
183 184
field_redirect_existing_links: Перенаправить существующие ссылки
184 185
field_regexp: Регулярное выражение
......
227 228
gui_validation_error_plural: %d ошибок
228 229

  
229 230
label_activity: Активность
231
label_access_denied: Доступ запрещен
230 232
label_add_another_file: Добавить ещё один файл
231 233
label_added_time_by: Добавил(а) %s %s назад
232 234
label_added: добавлено
......
430 432
label_open_issues_plural5: открыто
431 433
label_open_issues_plural: открыто
432 434
label_open_issues: открыт
435
label_private_issues_plural2: конфиденциальных
436
label_private_issues_plural5: конфиденциальных
437
label_private_issues_plural: конфиденциальных
438
label_private_issues: конфиденциальная
433 439
label_optional_description: Описание (опционально)
434 440
label_options: Опции
435 441
label_overall_activity: Сводная активность
......
622 628
permission_view_documents: Просмотр документов
623 629
permission_edit_project: Редактирование проектов
624 630
permission_add_issue_notes: Добавление примечаний
631
permission_add_private_issues: Добавление конфиденциальных задач
632
permission_view_private_issues: Просмотр конфиденциальных задач
625 633
permission_save_queries: Сохранение запросов
626 634
permission_view_wiki_pages: Просмотр wiki
627 635
permission_rename_wiki_pages: Переименование страниц wiki
redmine-0.8.0/lib/redmine.rb 2009-01-02 00:47:01.000000000 +0300
35 35
                                  :queries => :index,
36 36
                                  :reports => :issue_report}, :public => true                    
37 37
    map.permission :add_issues, {:issues => :new}
38
    map.permission :add_private_issues, {:issues => :new}
39
    map.permission :view_private_issues, {:projects => [:changelog, :roadmap],
40
                                  :issues => [:index, :changes, :show, :context_menu],
41
                                  :versions => [:show, :status_by],
42
                                  :queries => :index,
43
                                  :reports => :issue_report}
38 44
    map.permission :edit_issues, {:issues => [:edit, :reply, :bulk_edit, :destroy_attachment]}
39 45
    map.permission :manage_issue_relations, {:issue_relations => [:new, :destroy]}
40 46
    map.permission :add_issue_notes, {:issues => [:edit, :reply]}
redmine-0.8.0/test/fixtures/issues.yml 2009-01-07 20:17:43.000000000 +0300
108 108
  start_date: <%= 10.days.ago.to_s(:db) %>
109 109
  due_date: <%= Date.today.to_s(:db) %>
110 110
  lock_version: 0
111
  
111
issues_008: 
112
  created_on: <%= 5.days.ago.to_date.to_s(:db) %>
113
  project_id: 1
114
  updated_on: <%= 2.days.ago.to_date.to_s(:db) %>
115
  priority_id: 5
116
  subject: Private Issue on project 2
117
  id: 8
118
  fixed_version_id: 
119
  category_id: 
120
  description: Priavte Issue on project 2
121
  tracker_id: 1
122
  assigned_to_id: 
123
  author_id: 2
124
  status_id: 1
125
  private: 1
126
  
redmine-0.8.0/test/fixtures/roles.yml 2009-01-06 22:52:31.000000000 +0300
45 45
    - :browse_repository
46 46
    - :manage_repository
47 47
    - :view_changesets
48
    - :add_private_issues
49
    - :view_private_issues
48 50

  
49 51
  position: 1
50 52
roles_002: 
......
87 89
    - :manage_files
88 90
    - :browse_repository
89 91
    - :view_changesets
92
    - :view_private_issues
90 93

  
91 94
  position: 2
92 95
roles_003: 
......
173 176
    - :view_changesets
174 177

  
175 178
  position: 5
176
  
179
  
redmine-0.8.0/test/functional/issues_controller_test.rb 2009-01-07 23:01:30.000000000 +0300
252 252
                                            :content => /Notes/ } }
253 253
  end
254 254

  
255
  def test_show_private_issue_by_manager
256
    @request.session[:user_id] = 2
257
    get :show, :id => 8
258
    assert_response :success
259
    assert_tag :input, :attributes => { :name => 'issue[private]'}
260
    assert_tag :td, :attributes => { :class => 'private-issue'}
261
  end
262

  
263
  def test_show_private_issue_by_admin
264
    @request.session[:user_id] = 1
265
    get :show, :id => 8
266
    assert_response :success
267
    assert_tag :input, :attributes => { :name => 'issue[private]'}
268
    assert_tag :td, :attributes => { :class => 'private-issue'}
269
  end
270

  
271
  def test_show_private_issue_by_developer
272
    @request.session[:user_id] = 3
273
    get :show, :id => 8
274
    assert_response :success
275
    # Developer can't change issue type
276
    assert_no_tag :input, :attributes => { :name => 'issue[private]'}
277
    # Developer can view private issues
278
    assert_tag :td, :attributes => { :class => 'private-issue'}
279
  end
280

  
281
  def test_show_private_issue_by_non_member
282
    @request.session[:user_id] = 4
283
    get :show, :id => 8
284
    assert_response :success
285
    assert_no_tag :input, :attributes => { :name => 'issue[private]'}
286
    assert_no_tag :td, :attributes => { :class => 'private-issue'}
287
    assert_tag :p, :attributes => { :class => 'nodata'}
288
  end
289

  
290
  def test_show_private_issue_by_anonymous
291
    get :show, :id => 8
292
    assert_response :success
293
    assert_no_tag :input, :attributes => { :name => 'issue[private]'}
294
    assert_no_tag :td, :attributes => { :class => 'private-issue'}
295
    assert_tag :p, :attributes => { :class => 'nodata'}
296
  end
297

  
255 298
  def test_get_new
256 299
    @request.session[:user_id] = 2
257 300
    get :new, :project_id => 1, :tracker_id => 1
redmine-0.8.0/test/functional/projects_controller_test.rb 2009-01-07 23:05:32.000000000 +0300
202 202
                 }
203 203
               }
204 204
  end
205

  
206
  #private issue are not visible for Anonymous user in global Activity
207
  def test_private_issue_global_activity_for_anonymous
208
    get :activity
209
    assert_response :success
210
    assert_template 'activity'
211
    assert_not_nil assigns(:events_by_day)
212

  
213
    assert_no_tag :tag => "h3",
214
               :content => /#{5.day.ago.to_date.day}/,
215
               :sibling => { :tag => "dl",
216
                 :child => { :tag => "dt",
217
                   :attributes => { :class => /issue/ },
218
                   :child => { :tag => "a",
219
                     :content => /#{Issue.find(8).subject}/,
220
                   }
221
                 }
222
               }
223
  end
224

  
225
  def test_private_issue_global_activity_for_manager
226
    @request.session[:user_id] = 2 # manager
227
    get :activity
228
    assert_response :success
229
    assert_template 'activity'
230
    assert_not_nil assigns(:events_by_day)
231

  
232
    assert_tag :tag => "h3",
233
               :content => /#{5.day.ago.to_date.day}/,
234
               :sibling => { :tag => "dl",
235
                 :child => { :tag => "dt",
236
                   :attributes => { :class => /issue/ },
237
                   :child => { :tag => "a",
238
                     :content => /#{Issue.find(8).subject}/,
239
                   }
240
                 }
241
               }
242
  end
205 243
  
244
  def test_private_issue_global_activity_for_developer
245
    @request.session[:user_id] = 3 # developer
246
    get :activity
247
    assert_response :success
248
    assert_template 'activity'
249
    assert_not_nil assigns(:events_by_day)
250

  
251
    assert_tag :tag => "h3",
252
               :content => /#{5.day.ago.to_date.day}/,
253
               :sibling => { :tag => "dl",
254
                 :child => { :tag => "dt",
255
                   :attributes => { :class => /issue/ },
256
                   :child => { :tag => "a",
257
                     :content => /#{Issue.find(8).subject}/,
258
                   }
259
                 }
260
               }
261
  end
262

  
263
  def test_private_issue_global_activity_for_non_member
264
    @request.session[:user_id] = 4 # does not have any role in project #1
265
    get :activity
266
    assert_response :success
267
    assert_template 'activity'
268
    assert_not_nil assigns(:events_by_day)
269

  
270
    assert_no_tag :tag => "h3",
271
               :content => /#{5.day.ago.to_date.day}/,
272
               :sibling => { :tag => "dl",
273
                 :child => { :tag => "dt",
274
                   :attributes => { :class => /issue/ },
275
                   :child => { :tag => "a",
276
                     :content => /#{Issue.find(8).subject}/,
277
                   }
278
                 }
279
               }
280
  end
281

  
206 282
  def test_user_activity
207 283
    get :activity, :user_id => 2
208 284
    assert_response :success
redmine-0.8.0/test/unit/issue_test.rb 2009-01-07 01:12:05.000000000 +0300
197 197
    assert !Issue.new(:due_date => 1.day.from_now.to_date).overdue?
198 198
    assert !Issue.new(:due_date => nil).overdue?
199 199
  end
200

  
201
  def test_visible
202
    issue = Issue.new(:project_id => 1, :tracker_id => 1, :author_id => 2, :status_id => 1, :priority => Enumeration.get_values('IPRI').first, :subject => 'test_private_create', :description => 'IssueTest#test_private_create', :estimated_hours => '5:30', :private => true)
203
    assert issue.save
204
    issue.reload
205
    assert_equal true, issue.private
206
    # Test fixtures contain "add_private_issues" and "view_private_issues"
207
    # permissions for Manager role and only "view_private_issues" for Developer.
208
    # User with id #3 in project with id #1 has Developer role
209
    assert_equal true, issue.visible?(User.find(3), Project.find(1))
210
    # User with id #2 in project with id #1 has Manager role
211
    assert_equal true, issue.visible?(User.find(2), Project.find(1))
212
    # User with id #6 has Anonymous role
213
    assert_equal false, issue.visible?(User.find(6), Project.find(1))
214
    # User with id #4 does not have any role in project #1
215
    assert_equal false, issue.visible?(User.find(4), Project.find(1))
216
  end
200 217
end
(2-2/17)