@@ -0,0 +1,30 | |||||
|
1 | # Redmine - project management software | |||
|
2 | # Copyright (C) 2006-2014 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 | class GroupAnonymous < GroupBuiltin | |||
|
19 | def name | |||
|
20 | l(:label_group_anonymous) | |||
|
21 | end | |||
|
22 | ||||
|
23 | def builtin_type | |||
|
24 | "anonymous" | |||
|
25 | end | |||
|
26 | ||||
|
27 | def self.instance_id | |||
|
28 | @@instance_id ||= load_instance.id | |||
|
29 | end | |||
|
30 | end |
@@ -0,0 +1,56 | |||||
|
1 | # Redmine - project management software | |||
|
2 | # Copyright (C) 2006-2014 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 | class GroupBuiltin < Group | |||
|
19 | validate :validate_uniqueness, :on => :create | |||
|
20 | ||||
|
21 | def validate_uniqueness | |||
|
22 | errors.add :base, 'The builtin group already exists.' if self.class.exists? | |||
|
23 | end | |||
|
24 | ||||
|
25 | def builtin? | |||
|
26 | true | |||
|
27 | end | |||
|
28 | ||||
|
29 | def destroy | |||
|
30 | false | |||
|
31 | end | |||
|
32 | ||||
|
33 | def user_added(user) | |||
|
34 | raise 'Cannot add users to a builtin group' | |||
|
35 | end | |||
|
36 | ||||
|
37 | class << self | |||
|
38 | def load_instance | |||
|
39 | return nil if self == GroupBuiltin | |||
|
40 | instance = first(:order => 'id') || create_instance | |||
|
41 | end | |||
|
42 | ||||
|
43 | def create_instance | |||
|
44 | raise 'The builtin group already exists.' if exists? | |||
|
45 | instance = new | |||
|
46 | instance.lastname = name | |||
|
47 | instance.save :validate => false | |||
|
48 | raise 'Unable to create builtin group.' if instance.new_record? | |||
|
49 | instance | |||
|
50 | end | |||
|
51 | private :create_instance | |||
|
52 | end | |||
|
53 | end | |||
|
54 | ||||
|
55 | require_dependency "group_anonymous" | |||
|
56 | require_dependency "group_non_member" |
@@ -0,0 +1,30 | |||||
|
1 | # Redmine - project management software | |||
|
2 | # Copyright (C) 2006-2014 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 | class GroupNonMember < GroupBuiltin | |||
|
19 | def name | |||
|
20 | l(:label_group_non_member) | |||
|
21 | end | |||
|
22 | ||||
|
23 | def builtin_type | |||
|
24 | "non_member" | |||
|
25 | end | |||
|
26 | ||||
|
27 | def self.instance_id | |||
|
28 | @@instance_id ||= load_instance.id | |||
|
29 | end | |||
|
30 | end |
@@ -0,0 +1,21 | |||||
|
1 | class InsertBuiltinGroups < ActiveRecord::Migration | |||
|
2 | def up | |||
|
3 | Group.reset_column_information | |||
|
4 | ||||
|
5 | unless GroupAnonymous.any? | |||
|
6 | g = GroupAnonymous.new(:lastname => 'Anonymous users') | |||
|
7 | g.status = 1 | |||
|
8 | g.save :validate => false | |||
|
9 | end | |||
|
10 | unless GroupNonMember.any? | |||
|
11 | g = GroupNonMember.new(:lastname => 'Non member users') | |||
|
12 | g.status = 1 | |||
|
13 | g.save :validate => false | |||
|
14 | end | |||
|
15 | end | |||
|
16 | ||||
|
17 | def down | |||
|
18 | GroupAnonymous.delete_all | |||
|
19 | GroupNonMember.delete_all | |||
|
20 | end | |||
|
21 | end |
@@ -25,12 +25,16 class GroupsController < ApplicationController | |||||
25 | helper :custom_fields |
|
25 | helper :custom_fields | |
26 |
|
26 | |||
27 | def index |
|
27 | def index | |
28 | @groups = Group.sorted.all |
|
|||
29 | respond_to do |format| |
|
28 | respond_to do |format| | |
30 | format.html { |
|
29 | format.html { | |
|
30 | @groups = Group.sorted.all | |||
31 | @user_count_by_group_id = user_count_by_group_id |
|
31 | @user_count_by_group_id = user_count_by_group_id | |
32 | } |
|
32 | } | |
33 | format.api |
|
33 | format.api { | |
|
34 | scope = Group.sorted | |||
|
35 | scope = scope.givable unless params[:builtin] == '1' | |||
|
36 | @groups = scope.all | |||
|
37 | } | |||
34 | end |
|
38 | end | |
35 | end |
|
39 | end | |
36 |
|
40 |
@@ -18,11 +18,12 | |||||
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 |
|
19 | |||
20 | module GroupsHelper |
|
20 | module GroupsHelper | |
21 | def group_settings_tabs |
|
21 | def group_settings_tabs(group) | |
22 | tabs = [{:name => 'general', :partial => 'groups/general', :label => :label_general}, |
|
22 | tabs = [] | |
23 |
|
|
23 | tabs << {:name => 'general', :partial => 'groups/general', :label => :label_general} | |
24 |
|
|
24 | tabs << {:name => 'users', :partial => 'groups/users', :label => :label_user_plural} if group.givable? | |
25 | ] |
|
25 | tabs << {:name => 'memberships', :partial => 'groups/memberships', :label => :label_project_plural} | |
|
26 | tabs | |||
26 | end |
|
27 | end | |
27 |
|
28 | |||
28 | def render_principals_for_new_group_users(group) |
|
29 | def render_principals_for_new_group_users(group) |
@@ -46,7 +46,7 module UsersHelper | |||||
46 | tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general}, |
|
46 | tabs = [{:name => 'general', :partial => 'users/general', :label => :label_general}, | |
47 | {:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural} |
|
47 | {:name => 'memberships', :partial => 'users/memberships', :label => :label_project_plural} | |
48 | ] |
|
48 | ] | |
49 |
if Group. |
|
49 | if Group.givable.any? | |
50 | tabs.insert 1, {:name => 'groups', :partial => 'users/groups', :label => :label_group_plural} |
|
50 | tabs.insert 1, {:name => 'groups', :partial => 'users/groups', :label => :label_group_plural} | |
51 | end |
|
51 | end | |
52 | tabs |
|
52 | tabs |
@@ -31,17 +31,18 class Group < Principal | |||||
31 |
|
31 | |||
32 | before_destroy :remove_references_before_destroy |
|
32 | before_destroy :remove_references_before_destroy | |
33 |
|
33 | |||
34 | scope :sorted, lambda { order("#{table_name}.lastname ASC") } |
|
34 | scope :sorted, lambda { order("#{table_name}.type, #{table_name}.lastname ASC") } | |
35 | scope :named, lambda {|arg| where("LOWER(#{table_name}.lastname) = LOWER(?)", arg.to_s.strip)} |
|
35 | scope :named, lambda {|arg| where("LOWER(#{table_name}.lastname) = LOWER(?)", arg.to_s.strip)} | |
|
36 | scope :givable, lambda {where(:type => 'Group')} | |||
36 |
|
37 | |||
37 | safe_attributes 'name', |
|
38 | safe_attributes 'name', | |
38 | 'user_ids', |
|
39 | 'user_ids', | |
39 | 'custom_field_values', |
|
40 | 'custom_field_values', | |
40 | 'custom_fields', |
|
41 | 'custom_fields', | |
41 | :if => lambda {|group, user| user.admin?} |
|
42 | :if => lambda {|group, user| user.admin? && !group.builtin?} | |
42 |
|
43 | |||
43 | def to_s |
|
44 | def to_s | |
44 |
|
|
45 | name.to_s | |
45 | end |
|
46 | end | |
46 |
|
47 | |||
47 | def name |
|
48 | def name | |
@@ -52,6 +53,20 class Group < Principal | |||||
52 | self.lastname = arg |
|
53 | self.lastname = arg | |
53 | end |
|
54 | end | |
54 |
|
55 | |||
|
56 | def builtin_type | |||
|
57 | nil | |||
|
58 | end | |||
|
59 | ||||
|
60 | # Return true if the group is a builtin group | |||
|
61 | def builtin? | |||
|
62 | false | |||
|
63 | end | |||
|
64 | ||||
|
65 | # Returns true if the group can be given to a user | |||
|
66 | def givable? | |||
|
67 | !builtin? | |||
|
68 | end | |||
|
69 | ||||
55 | def user_added(user) |
|
70 | def user_added(user) | |
56 | members.each do |member| |
|
71 | members.each do |member| | |
57 | next if member.project.nil? |
|
72 | next if member.project.nil? | |
@@ -80,6 +95,18 class Group < Principal | |||||
80 | super(attr_name, *args) |
|
95 | super(attr_name, *args) | |
81 | end |
|
96 | end | |
82 |
|
97 | |||
|
98 | def self.builtin_id(arg) | |||
|
99 | (arg.anonymous? ? GroupAnonymous : GroupNonMember).instance_id | |||
|
100 | end | |||
|
101 | ||||
|
102 | def self.anonymous | |||
|
103 | GroupAnonymous.load_instance | |||
|
104 | end | |||
|
105 | ||||
|
106 | def self.non_member | |||
|
107 | GroupNonMember.load_instance | |||
|
108 | end | |||
|
109 | ||||
83 | private |
|
110 | private | |
84 |
|
111 | |||
85 | # Removes references that are not handled by associations |
|
112 | # Removes references that are not handled by associations | |
@@ -89,3 +116,5 class Group < Principal | |||||
89 | Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL') |
|
116 | Issue.where(['assigned_to_id = ?', id]).update_all('assigned_to_id = NULL') | |
90 | end |
|
117 | end | |
91 | end |
|
118 | end | |
|
119 | ||||
|
120 | require_dependency "group_builtin" |
@@ -147,6 +147,7 class IssueQuery < Query | |||||
147 | end |
|
147 | end | |
148 | principals.uniq! |
|
148 | principals.uniq! | |
149 | principals.sort! |
|
149 | principals.sort! | |
|
150 | principals.reject! {|p| p.is_a?(GroupBuiltin)} | |||
150 | users = principals.select {|p| p.is_a?(User)} |
|
151 | users = principals.select {|p| p.is_a?(User)} | |
151 |
|
152 | |||
152 | add_available_filter "status_id", |
|
153 | add_available_filter "status_id", | |
@@ -183,7 +184,7 class IssueQuery < Query | |||||
183 | :type => :list_optional, :values => assigned_to_values |
|
184 | :type => :list_optional, :values => assigned_to_values | |
184 | ) unless assigned_to_values.empty? |
|
185 | ) unless assigned_to_values.empty? | |
185 |
|
186 | |||
186 |
group_values = Group. |
|
187 | group_values = Group.givable.collect {|g| [g.name, g.id.to_s] } | |
187 | add_available_filter("member_of_group", |
|
188 | add_available_filter("member_of_group", | |
188 | :type => :list_optional, :values => group_values |
|
189 | :type => :list_optional, :values => group_values | |
189 | ) unless group_values.empty? |
|
190 | ) unless group_values.empty? | |
@@ -404,10 +405,10 class IssueQuery < Query | |||||
404 |
|
405 | |||
405 | def sql_for_member_of_group_field(field, operator, value) |
|
406 | def sql_for_member_of_group_field(field, operator, value) | |
406 | if operator == '*' # Any group |
|
407 | if operator == '*' # Any group | |
407 |
groups = Group. |
|
408 | groups = Group.givable | |
408 | operator = '=' # Override the operator since we want to find by assigned_to |
|
409 | operator = '=' # Override the operator since we want to find by assigned_to | |
409 | elsif operator == "!*" |
|
410 | elsif operator == "!*" | |
410 |
groups = Group. |
|
411 | groups = Group.givable | |
411 | operator = '!' # Override the operator since we want to find by assigned_to |
|
412 | operator = '!' # Override the operator since we want to find by assigned_to | |
412 | else |
|
413 | else | |
413 | groups = Group.where(:id => value).all |
|
414 | groups = Group.where(:id => value).all |
@@ -114,3 +114,6 class Principal < ActiveRecord::Base | |||||
114 | true |
|
114 | true | |
115 | end |
|
115 | end | |
116 | end |
|
116 | end | |
|
117 | ||||
|
118 | require_dependency "user" | |||
|
119 | require_dependency "group" |
@@ -32,7 +32,7 class Project < ActiveRecord::Base | |||||
32 | has_many :memberships, :class_name => 'Member' |
|
32 | has_many :memberships, :class_name => 'Member' | |
33 | has_many :member_principals, :class_name => 'Member', |
|
33 | has_many :member_principals, :class_name => 'Member', | |
34 | :include => :principal, |
|
34 | :include => :principal, | |
35 |
:conditions => "#{Principal.table_name} |
|
35 | :conditions => "#{Principal.table_name}.status=#{Principal::STATUS_ACTIVE}" | |
36 |
|
36 | |||
37 | has_many :enabled_modules, :dependent => :delete_all |
|
37 | has_many :enabled_modules, :dependent => :delete_all | |
38 | has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position" |
|
38 | has_and_belongs_to_many :trackers, :order => "#{Tracker.table_name}.position" | |
@@ -191,11 +191,9 class Project < ActiveRecord::Base | |||||
191 | statement_by_role[role] = "#{Project.table_name}.is_public = #{connection.quoted_true}" |
|
191 | statement_by_role[role] = "#{Project.table_name}.is_public = #{connection.quoted_true}" | |
192 | end |
|
192 | end | |
193 | end |
|
193 | end | |
194 | if user.logged? |
|
194 | user.projects_by_role.each do |role, projects| | |
195 | user.projects_by_role.each do |role, projects| |
|
195 | if role.allowed_to?(permission) && projects.any? | |
196 | if role.allowed_to?(permission) && projects.any? |
|
196 | statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})" | |
197 | statement_by_role[role] = "#{Project.table_name}.id IN (#{projects.collect(&:id).join(',')})" |
|
|||
198 | end |
|
|||
199 | end |
|
197 | end | |
200 | end |
|
198 | end | |
201 | if statement_by_role.empty? |
|
199 | if statement_by_role.empty? | |
@@ -213,6 +211,12 class Project < ActiveRecord::Base | |||||
213 | end |
|
211 | end | |
214 | end |
|
212 | end | |
215 |
|
213 | |||
|
214 | def override_roles(role) | |||
|
215 | @override_members ||= memberships.where(:user_id => [GroupAnonymous.instance_id, GroupNonMember.instance_id]).all | |||
|
216 | member = @override_members.detect {|m| role.anonymous? ^ (m.user_id == GroupNonMember.instance_id)} | |||
|
217 | member ? member.roles : [role] | |||
|
218 | end | |||
|
219 | ||||
216 | def principals |
|
220 | def principals | |
217 | @principals ||= Principal.active.joins(:members).where("#{Member.table_name}.project_id = ?", id).uniq |
|
221 | @principals ||= Principal.active.joins(:members).where("#{Member.table_name}.project_id = ?", id).uniq | |
218 | end |
|
222 | end | |
@@ -305,6 +309,7 class Project < ActiveRecord::Base | |||||
305 | @actions_allowed = nil |
|
309 | @actions_allowed = nil | |
306 | @start_date = nil |
|
310 | @start_date = nil | |
307 | @due_date = nil |
|
311 | @due_date = nil | |
|
312 | @override_members = nil | |||
308 | base_reload(*args) |
|
313 | base_reload(*args) | |
309 | end |
|
314 | end | |
310 |
|
315 | |||
@@ -498,8 +503,13 class Project < ActiveRecord::Base | |||||
498 |
|
503 | |||
499 | # Users/groups issues can be assigned to |
|
504 | # Users/groups issues can be assigned to | |
500 | def assignable_users |
|
505 | def assignable_users | |
501 | assignable = Setting.issue_group_assignment? ? member_principals : members |
|
506 | types = ['User'] | |
502 | assignable.select {|m| m.roles.detect {|role| role.assignable?}}.collect {|m| m.principal}.sort |
|
507 | types << 'Group' if Setting.issue_group_assignment? | |
|
508 | ||||
|
509 | member_principals. | |||
|
510 | select {|m| types.include?(m.principal.type) && m.roles.detect(&:assignable?)}. | |||
|
511 | map(&:principal). | |||
|
512 | sort | |||
503 | end |
|
513 | end | |
504 |
|
514 | |||
505 | # Returns the mail addresses of users that should be always notified on project events |
|
515 | # Returns the mail addresses of users that should be always notified on project events |
@@ -474,15 +474,15 class User < Principal | |||||
474 |
|
474 | |||
475 | # Return user's roles for project |
|
475 | # Return user's roles for project | |
476 | def roles_for_project(project) |
|
476 | def roles_for_project(project) | |
477 | roles = [] |
|
|||
478 | # No role on archived projects |
|
477 | # No role on archived projects | |
479 |
return |
|
478 | return [] if project.nil? || project.archived? | |
480 | if membership = membership(project) |
|
479 | if membership = membership(project) | |
481 |
|
|
480 | membership.roles.dup | |
|
481 | elsif project.is_public? | |||
|
482 | project.override_roles(builtin_role) | |||
482 | else |
|
483 | else | |
483 | roles << builtin_role |
|
484 | [] | |
484 | end |
|
485 | end | |
485 | roles |
|
|||
486 | end |
|
486 | end | |
487 |
|
487 | |||
488 | # Return true if the user is a member of project |
|
488 | # Return true if the user is a member of project | |
@@ -494,20 +494,28 class User < Principal | |||||
494 | def projects_by_role |
|
494 | def projects_by_role | |
495 | return @projects_by_role if @projects_by_role |
|
495 | return @projects_by_role if @projects_by_role | |
496 |
|
496 | |||
497 |
|
|
497 | hash = Hash.new([]) | |
498 | memberships.each do |membership| |
|
498 | ||
499 | if membership.project |
|
499 | members = Member.joins(:project). | |
500 | membership.roles.each do |role| |
|
500 | where("#{Project.table_name}.status <> 9"). | |
501 | @projects_by_role[role] = [] unless @projects_by_role.key?(role) |
|
501 | where("#{Member.table_name}.user_id = ? OR (#{Project.table_name}.is_public = ? AND #{Member.table_name}.user_id = ?)", self.id, true, Group.builtin_id(self)). | |
502 | @projects_by_role[role] << membership.project |
|
502 | preload(:project, :roles) | |
|
503 | ||||
|
504 | members.reject! {|member| member.user_id != id && project_ids.include?(member.project_id)} | |||
|
505 | members.each do |member| | |||
|
506 | if member.project | |||
|
507 | member.roles.each do |role| | |||
|
508 | hash[role] = [] unless hash.key?(role) | |||
|
509 | hash[role] << member.project | |||
503 | end |
|
510 | end | |
504 | end |
|
511 | end | |
505 | end |
|
512 | end | |
506 | @projects_by_role.each do |role, projects| |
|
513 | ||
|
514 | hash.each do |role, projects| | |||
507 | projects.uniq! |
|
515 | projects.uniq! | |
508 | end |
|
516 | end | |
509 |
|
517 | |||
510 | @projects_by_role |
|
518 | @projects_by_role = hash | |
511 | end |
|
519 | end | |
512 |
|
520 | |||
513 | # Returns true if user is arg or belongs to arg |
|
521 | # Returns true if user is arg or belongs to arg |
@@ -1,7 +1,9 | |||||
1 | <%= error_messages_for @group %> |
|
1 | <%= error_messages_for @group %> | |
2 |
|
2 | |||
3 | <div class="box tabular"> |
|
3 | <div class="box tabular"> | |
4 |
<p><%= f.text_field :name, :required => true, :size => 60 |
|
4 | <p><%= f.text_field :name, :required => true, :size => 60, | |
|
5 | :disabled => !@group.safe_attribute?('name') %></p> | |||
|
6 | ||||
5 | <% @group.custom_field_values.each do |value| %> |
|
7 | <% @group.custom_field_values.each do |value| %> | |
6 | <p><%= custom_field_tag_with_label :group, value %></p> |
|
8 | <p><%= custom_field_tag_with_label :group, value %></p> | |
7 | <% end %> |
|
9 | <% end %> |
@@ -1,4 +1,4 | |||||
1 | <%= labelled_form_for @group do |f| %> |
|
1 | <%= labelled_form_for @group, :url => group_path(@group) do |f| %> | |
2 | <%= render :partial => 'form', :locals => { :f => f } %> |
|
2 | <%= render :partial => 'form', :locals => { :f => f } %> | |
3 | <%= submit_tag l(:button_save) %> |
|
3 | <%= submit_tag l(:button_save) %> | |
4 | <% end %> |
|
4 | <% end %> |
@@ -1,3 +1,3 | |||||
1 | <%= title [l(:label_group_plural), groups_path], @group.name %> |
|
1 | <%= title [l(:label_group_plural), groups_path], @group.name %> | |
2 |
|
2 | |||
3 | <%= render_tabs group_settings_tabs %> |
|
3 | <%= render_tabs group_settings_tabs(@group) %> |
@@ -3,6 +3,7 api.array :groups do | |||||
3 | api.group do |
|
3 | api.group do | |
4 | api.id group.id |
|
4 | api.id group.id | |
5 | api.name group.lastname |
|
5 | api.name group.lastname | |
|
6 | api.builtin group.builtin_type if group.builtin_type | |||
6 |
|
7 | |||
7 | render_api_custom_values group.visible_custom_field_values, api |
|
8 | render_api_custom_values group.visible_custom_field_values, api | |
8 | end |
|
9 | end |
@@ -12,10 +12,10 | |||||
12 | </tr></thead> |
|
12 | </tr></thead> | |
13 | <tbody> |
|
13 | <tbody> | |
14 | <% @groups.each do |group| %> |
|
14 | <% @groups.each do |group| %> | |
15 | <tr id="group-<%= group.id %>" class="<%= cycle 'odd', 'even' %>"> |
|
15 | <tr id="group-<%= group.id %>" class="<%= cycle 'odd', 'even' %> <%= "builtin" if group.builtin? %>"> | |
16 | <td class="name"><%= link_to h(group), edit_group_path(group) %></td> |
|
16 | <td class="name"><%= link_to h(group), edit_group_path(group) %></td> | |
17 | <td class="user_count"><%= @user_count_by_group_id[group.id] || 0 %></td> |
|
17 | <td class="user_count"><%= (@user_count_by_group_id[group.id] || 0) unless group.builtin? %></td> | |
18 | <td class="buttons"><%= delete_link group %></td> |
|
18 | <td class="buttons"><%= delete_link group unless group.builtin? %></td> | |
19 | </tr> |
|
19 | </tr> | |
20 | <% end %> |
|
20 | <% end %> | |
21 | </tbody> |
|
21 | </tbody> |
@@ -1,6 +1,7 | |||||
1 | api.group do |
|
1 | api.group do | |
2 | api.id @group.id |
|
2 | api.id @group.id | |
3 | api.name @group.lastname |
|
3 | api.name @group.lastname | |
|
4 | api.builtin @group.builtin_type if @group.builtin_type | |||
4 |
|
5 | |||
5 | render_api_custom_values @group.visible_custom_field_values, api |
|
6 | render_api_custom_values @group.visible_custom_field_values, api | |
6 |
|
7 | |||
@@ -8,7 +9,7 api.group do | |||||
8 | @group.users.each do |user| |
|
9 | @group.users.each do |user| | |
9 | api.user :id => user.id, :name => user.name |
|
10 | api.user :id => user.id, :name => user.name | |
10 | end |
|
11 | end | |
11 | end if include_in_api_response?('users') |
|
12 | end if include_in_api_response?('users') && !@group.builtin? | |
12 |
|
13 | |||
13 | api.array :memberships do |
|
14 | api.array :memberships do | |
14 | @group.memberships.each do |membership| |
|
15 | @group.memberships.each do |membership| |
@@ -1,6 +1,6 | |||||
1 | <%= form_for(:user, :url => { :action => 'update' }, :html => {:method => :put}) do %> |
|
1 | <%= form_for(:user, :url => { :action => 'update' }, :html => {:method => :put}) do %> | |
2 | <div class="box"> |
|
2 | <div class="box"> | |
3 |
<% Group. |
|
3 | <% Group.givable.sort.each do |group| %> | |
4 | <label><%= check_box_tag 'user[group_ids][]', group.id, @user.groups.include?(group), :id => nil %> <%=h group %></label><br /> |
|
4 | <label><%= check_box_tag 'user[group_ids][]', group.id, @user.groups.include?(group), :id => nil %> <%=h group %></label><br /> | |
5 | <% end %> |
|
5 | <% end %> | |
6 | <%= hidden_field_tag 'user[group_ids][]', '' %> |
|
6 | <%= hidden_field_tag 'user[group_ids][]', '' %> |
@@ -851,6 +851,8 en: | |||||
851 | label_group: Group |
|
851 | label_group: Group | |
852 | label_group_plural: Groups |
|
852 | label_group_plural: Groups | |
853 | label_group_new: New group |
|
853 | label_group_new: New group | |
|
854 | label_group_anonymous: Anonymous users | |||
|
855 | label_group_non_member: Non member users | |||
854 | label_time_entry_plural: Spent time |
|
856 | label_time_entry_plural: Spent time | |
855 | label_version_sharing_none: Not shared |
|
857 | label_version_sharing_none: Not shared | |
856 | label_version_sharing_descendants: With subprojects |
|
858 | label_version_sharing_descendants: With subprojects |
@@ -871,6 +871,8 fr: | |||||
871 | label_group: Groupe |
|
871 | label_group: Groupe | |
872 | label_group_plural: Groupes |
|
872 | label_group_plural: Groupes | |
873 | label_group_new: Nouveau groupe |
|
873 | label_group_new: Nouveau groupe | |
|
874 | label_group_anonymous: Utilisateurs anonymes | |||
|
875 | label_group_non_member: Utilisateurs non membres | |||
874 | label_time_entry_plural: Temps passΓ© |
|
876 | label_time_entry_plural: Temps passΓ© | |
875 | label_version_sharing_none: Non partagΓ© |
|
877 | label_version_sharing_none: Non partagΓ© | |
876 | label_version_sharing_descendants: Avec les sous-projets |
|
878 | label_version_sharing_descendants: Avec les sous-projets |
@@ -248,7 +248,12 sub RedmineDSN { | |||||
248 | AND ( |
|
248 | AND ( | |
249 | roles.id IN (SELECT member_roles.role_id FROM members, member_roles WHERE members.user_id = users.id AND members.project_id = projects.id AND members.id = member_roles.member_id) |
|
249 | roles.id IN (SELECT member_roles.role_id FROM members, member_roles WHERE members.user_id = users.id AND members.project_id = projects.id AND members.id = member_roles.member_id) | |
250 | OR |
|
250 | OR | |
251 |
( |
|
251 | (cast(projects.is_public as CHAR) IN ('t', '1') | |
|
252 | AND (roles.builtin=1 | |||
|
253 | OR roles.id IN (SELECT member_roles.role_id FROM members, member_roles, users g | |||
|
254 | WHERE members.user_id = g.id AND members.project_id = projects.id AND members.id = member_roles.member_id | |||
|
255 | AND g.type = 'GroupNonMember')) | |||
|
256 | ) | |||
252 | ) |
|
257 | ) | |
253 | AND roles.permissions IS NOT NULL"; |
|
258 | AND roles.permissions IS NOT NULL"; | |
254 | $self->{RedmineQuery} = trim($query); |
|
259 | $self->{RedmineQuery} = trim($query); | |
@@ -328,7 +333,7 sub access_handler { | |||||
328 | my $project_id = get_project_identifier($r); |
|
333 | my $project_id = get_project_identifier($r); | |
329 |
|
334 | |||
330 | $r->set_handlers(PerlAuthenHandler => [\&OK]) |
|
335 | $r->set_handlers(PerlAuthenHandler => [\&OK]) | |
331 |
if is_public_project($project_id, $r) && anonymous_ |
|
336 | if is_public_project($project_id, $r) && anonymous_allowed_to_browse_repository($project_id, $r); | |
332 |
|
337 | |||
333 | return OK |
|
338 | return OK | |
334 | } |
|
339 | } | |
@@ -400,15 +405,20 sub is_public_project { | |||||
400 | $ret; |
|
405 | $ret; | |
401 | } |
|
406 | } | |
402 |
|
407 | |||
403 |
sub anonymous_ |
|
408 | sub anonymous_allowed_to_browse_repository { | |
|
409 | my $project_id = shift; | |||
404 | my $r = shift; |
|
410 | my $r = shift; | |
405 |
|
411 | |||
406 | my $dbh = connect_database($r); |
|
412 | my $dbh = connect_database($r); | |
407 | my $sth = $dbh->prepare( |
|
413 | my $sth = $dbh->prepare( | |
408 |
"SELECT permissions FROM roles WHERE |
|
414 | "SELECT permissions FROM roles WHERE permissions like '%browse_repository%' | |
|
415 | AND (roles.builtin = 2 | |||
|
416 | OR roles.id IN (SELECT member_roles.role_id FROM projects, members, member_roles, users | |||
|
417 | WHERE members.user_id = users.id AND members.project_id = projects.id AND members.id = member_roles.member_id | |||
|
418 | AND projects.identifier = ? AND users.type = 'GroupAnonymous'));" | |||
409 | ); |
|
419 | ); | |
410 |
|
420 | |||
411 | $sth->execute(); |
|
421 | $sth->execute($project_id); | |
412 | my $ret = 0; |
|
422 | my $ret = 0; | |
413 | if (my @row = $sth->fetchrow_array) { |
|
423 | if (my @row = $sth->fetchrow_array) { | |
414 | if ($row[0] =~ /:browse_repository/) { |
|
424 | if ($row[0] =~ /:browse_repository/) { |
@@ -241,6 +241,8 table p {margin:0;} | |||||
241 | .odd {background-color:#f6f7f8;} |
|
241 | .odd {background-color:#f6f7f8;} | |
242 | .even {background-color: #fff;} |
|
242 | .even {background-color: #fff;} | |
243 |
|
243 | |||
|
244 | tr.builtin td.name {font-style:italic;} | |||
|
245 | ||||
244 | a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; } |
|
246 | a.sort { padding-right: 16px; background-position: 100% 50%; background-repeat: no-repeat; } | |
245 | a.sort.asc { background-image: url(../images/sort_asc.png); } |
|
247 | a.sort.asc { background-image: url(../images/sort_asc.png); } | |
246 | a.sort.desc { background-image: url(../images/sort_desc.png); } |
|
248 | a.sort.desc { background-image: url(../images/sort_desc.png); } | |
@@ -609,7 +611,8 select.bool_cf {width:auto !important;} | |||||
609 | #users_for_watcher {height: 200px; overflow:auto;} |
|
611 | #users_for_watcher {height: 200px; overflow:auto;} | |
610 | #users_for_watcher label {display: block;} |
|
612 | #users_for_watcher label {display: block;} | |
611 |
|
613 | |||
612 | table.members td.group { padding-left: 20px; background: url(../images/group.png) no-repeat 0% 50%; } |
|
614 | table.members td.name {padding-left: 20px;} | |
|
615 | table.members td.group, table.members td.groupnonmember, table.members td.groupanonymous {background: url(../images/group.png) no-repeat 0% 1px;} | |||
613 |
|
616 | |||
614 | input#principal_search, input#user_search {width:90%} |
|
617 | input#principal_search, input#user_search {width:90%} | |
615 |
|
618 |
@@ -27,6 +27,12 class RedminePmTest::RepositorySubversionTest < RedminePmTest::TestCase | |||||
27 | assert_success "ls", svn_url |
|
27 | assert_success "ls", svn_url | |
28 | end |
|
28 | end | |
29 |
|
29 | |||
|
30 | def test_anonymous_read_on_public_repo_with_anonymous_group_permission_should_succeed | |||
|
31 | Role.anonymous.remove_permission! :browse_repository | |||
|
32 | Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2]) | |||
|
33 | assert_success "ls", svn_url | |||
|
34 | end | |||
|
35 | ||||
30 | def test_anonymous_read_on_public_repo_without_permission_should_fail |
|
36 | def test_anonymous_read_on_public_repo_without_permission_should_fail | |
31 | Role.anonymous.remove_permission! :browse_repository |
|
37 | Role.anonymous.remove_permission! :browse_repository | |
32 | assert_failure "ls", svn_url |
|
38 | assert_failure "ls", svn_url | |
@@ -55,6 +61,15 class RedminePmTest::RepositorySubversionTest < RedminePmTest::TestCase | |||||
55 | end |
|
61 | end | |
56 | end |
|
62 | end | |
57 |
|
63 | |||
|
64 | def test_non_member_read_on_public_repo_with_non_member_group_permission_should_succeed | |||
|
65 | Role.anonymous.remove_permission! :browse_repository | |||
|
66 | Role.non_member.remove_permission! :browse_repository | |||
|
67 | Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2]) | |||
|
68 | with_credentials "miscuser8", "foo" do | |||
|
69 | assert_success "ls", svn_url | |||
|
70 | end | |||
|
71 | end | |||
|
72 | ||||
58 | def test_non_member_read_on_public_repo_without_permission_should_fail |
|
73 | def test_non_member_read_on_public_repo_without_permission_should_fail | |
59 | Role.anonymous.remove_permission! :browse_repository |
|
74 | Role.anonymous.remove_permission! :browse_repository | |
60 | Role.non_member.remove_permission! :browse_repository |
|
75 | Role.non_member.remove_permission! :browse_repository |
@@ -65,6 +65,7 module RedminePmTest | |||||
65 | @command = args.join(' ') |
|
65 | @command = args.join(' ') | |
66 | @status = nil |
|
66 | @status = nil | |
67 | IO.popen("#{command} 2>&1") do |io| |
|
67 | IO.popen("#{command} 2>&1") do |io| | |
|
68 | io.set_encoding("ASCII-8BIT") if io.respond_to?(:set_encoding) | |||
68 | @response = io.read |
|
69 | @response = io.read | |
69 | end |
|
70 | end | |
70 | @status = $?.exitstatus |
|
71 | @status = $?.exitstatus |
@@ -161,9 +161,20 groups_010: | |||||
161 | id: 10 |
|
161 | id: 10 | |
162 | lastname: A Team |
|
162 | lastname: A Team | |
163 | type: Group |
|
163 | type: Group | |
|
164 | status: 1 | |||
164 | groups_011: |
|
165 | groups_011: | |
165 | id: 11 |
|
166 | id: 11 | |
166 | lastname: B Team |
|
167 | lastname: B Team | |
167 | type: Group |
|
168 | type: Group | |
|
169 | status: 1 | |||
|
170 | groups_non_member: | |||
|
171 | id: 12 | |||
|
172 | lastname: Non member users | |||
|
173 | type: GroupNonMember | |||
|
174 | status: 1 | |||
|
175 | groups_anonymous: | |||
|
176 | id: 13 | |||
|
177 | lastname: Anonymous users | |||
|
178 | type: GroupAnonymous | |||
|
179 | status: 1 | |||
168 |
|
180 | |||
169 |
|
@@ -29,12 +29,13 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base | |||||
29 | assert_response 401 |
|
29 | assert_response 401 | |
30 | end |
|
30 | end | |
31 |
|
31 | |||
32 | test "GET /groups.xml should return groups" do |
|
32 | test "GET /groups.xml should return givable groups" do | |
33 | get '/groups.xml', {}, credentials('admin') |
|
33 | get '/groups.xml', {}, credentials('admin') | |
34 | assert_response :success |
|
34 | assert_response :success | |
35 | assert_equal 'application/xml', response.content_type |
|
35 | assert_equal 'application/xml', response.content_type | |
36 |
|
36 | |||
37 | assert_select 'groups' do |
|
37 | assert_select 'groups' do | |
|
38 | assert_select 'group', Group.givable.count | |||
38 | assert_select 'group' do |
|
39 | assert_select 'group' do | |
39 | assert_select 'name', :text => 'A Team' |
|
40 | assert_select 'name', :text => 'A Team' | |
40 | assert_select 'id', :text => '10' |
|
41 | assert_select 'id', :text => '10' | |
@@ -42,6 +43,24 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base | |||||
42 | end |
|
43 | end | |
43 | end |
|
44 | end | |
44 |
|
45 | |||
|
46 | test "GET /groups.xml?builtin=1 should return all groups" do | |||
|
47 | get '/groups.xml?builtin=1', {}, credentials('admin') | |||
|
48 | assert_response :success | |||
|
49 | assert_equal 'application/xml', response.content_type | |||
|
50 | ||||
|
51 | assert_select 'groups' do | |||
|
52 | assert_select 'group', Group.givable.count + 2 | |||
|
53 | assert_select 'group' do | |||
|
54 | assert_select 'builtin', :text => 'non_member' | |||
|
55 | assert_select 'id', :text => '12' | |||
|
56 | end | |||
|
57 | assert_select 'group' do | |||
|
58 | assert_select 'builtin', :text => 'anonymous' | |||
|
59 | assert_select 'id', :text => '13' | |||
|
60 | end | |||
|
61 | end | |||
|
62 | end | |||
|
63 | ||||
45 | test "GET /groups.json should require authentication" do |
|
64 | test "GET /groups.json should require authentication" do | |
46 | get '/groups.json' |
|
65 | get '/groups.json' | |
47 | assert_response 401 |
|
66 | assert_response 401 | |
@@ -60,7 +79,7 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base | |||||
60 | assert_equal({'id' => 10, 'name' => 'A Team'}, group) |
|
79 | assert_equal({'id' => 10, 'name' => 'A Team'}, group) | |
61 | end |
|
80 | end | |
62 |
|
81 | |||
63 |
test "GET /groups/:id.xml should return the group |
|
82 | test "GET /groups/:id.xml should return the group" do | |
64 | get '/groups/10.xml', {}, credentials('admin') |
|
83 | get '/groups/10.xml', {}, credentials('admin') | |
65 | assert_response :success |
|
84 | assert_response :success | |
66 | assert_equal 'application/xml', response.content_type |
|
85 | assert_equal 'application/xml', response.content_type | |
@@ -71,6 +90,17 class Redmine::ApiTest::GroupsTest < Redmine::ApiTest::Base | |||||
71 | end |
|
90 | end | |
72 | end |
|
91 | end | |
73 |
|
92 | |||
|
93 | test "GET /groups/:id.xml should return the builtin group" do | |||
|
94 | get '/groups/12.xml', {}, credentials('admin') | |||
|
95 | assert_response :success | |||
|
96 | assert_equal 'application/xml', response.content_type | |||
|
97 | ||||
|
98 | assert_select 'group' do | |||
|
99 | assert_select 'builtin', :text => 'non_member' | |||
|
100 | assert_select 'id', :text => '12' | |||
|
101 | end | |||
|
102 | end | |||
|
103 | ||||
74 | test "GET /groups/:id.xml should include users if requested" do |
|
104 | test "GET /groups/:id.xml should include users if requested" do | |
75 | get '/groups/10.xml?include=users', {}, credentials('admin') |
|
105 | get '/groups/10.xml?include=users', {}, credentials('admin') | |
76 | assert_response :success |
|
106 | assert_response :success |
@@ -65,6 +65,27 class IssuesTest < ActionController::IntegrationTest | |||||
65 | assert_equal 1, issue.status.id |
|
65 | assert_equal 1, issue.status.id | |
66 | end |
|
66 | end | |
67 |
|
67 | |||
|
68 | def test_create_issue_by_anonymous_without_permission_should_fail | |||
|
69 | Role.anonymous.remove_permission! :add_issues | |||
|
70 | ||||
|
71 | assert_no_difference 'Issue.count' do | |||
|
72 | post 'projects/1/issues', :tracker_id => "1", :issue => {:subject => "new test issue"} | |||
|
73 | end | |||
|
74 | assert_response 302 | |||
|
75 | end | |||
|
76 | ||||
|
77 | def test_create_issue_by_anonymous_with_custom_permission_should_succeed | |||
|
78 | Role.anonymous.remove_permission! :add_issues | |||
|
79 | Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [3]) | |||
|
80 | ||||
|
81 | assert_difference 'Issue.count' do | |||
|
82 | post 'projects/1/issues', :tracker_id => "1", :issue => {:subject => "new test issue"} | |||
|
83 | end | |||
|
84 | assert_response 302 | |||
|
85 | issue = Issue.order("id DESC").first | |||
|
86 | assert_equal User.anonymous, issue.author | |||
|
87 | end | |||
|
88 | ||||
68 | # add then remove 2 attachments to an issue |
|
89 | # add then remove 2 attachments to an issue | |
69 | def test_issue_attachments |
|
90 | def test_issue_attachments | |
70 | log_user('jsmith', 'jsmith') |
|
91 | log_user('jsmith', 'jsmith') |
@@ -133,4 +133,20 class GroupTest < ActiveSupport::TestCase | |||||
133 |
|
133 | |||
134 | assert_equal nil, Issue.find(1).assigned_to_id |
|
134 | assert_equal nil, Issue.find(1).assigned_to_id | |
135 | end |
|
135 | end | |
|
136 | ||||
|
137 | def test_builtin_id_with_anonymous_user_should_return_anonymous_group | |||
|
138 | assert_equal 13, Group.builtin_id(User.anonymous) | |||
|
139 | end | |||
|
140 | ||||
|
141 | def test_builtin_id_with_anonymous_role_should_return_anonymous_group | |||
|
142 | assert_equal 13, Group.builtin_id(Role.anonymous) | |||
|
143 | end | |||
|
144 | ||||
|
145 | def test_builtin_id_with_user_should_return_non_member_group | |||
|
146 | assert_equal 12, Group.builtin_id(User.find(1)) | |||
|
147 | end | |||
|
148 | ||||
|
149 | def test_builtin_id_with_non_member_role_should_return_non_member_group | |||
|
150 | assert_equal 12, Group.builtin_id(Role.non_member) | |||
|
151 | end | |||
136 | end |
|
152 | end |
@@ -219,6 +219,16 class IssueTest < ActiveSupport::TestCase | |||||
219 | assert_visibility_match User.anonymous, issues |
|
219 | assert_visibility_match User.anonymous, issues | |
220 | end |
|
220 | end | |
221 |
|
221 | |||
|
222 | def test_visible_scope_for_anonymous_without_view_issues_permissions_and_membership | |||
|
223 | Role.anonymous.remove_permission!(:view_issues) | |||
|
224 | Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [2]) | |||
|
225 | ||||
|
226 | issues = Issue.visible(User.anonymous).all | |||
|
227 | assert issues.any? | |||
|
228 | assert_equal [1], issues.map(&:project_id).uniq.sort | |||
|
229 | assert_visibility_match User.anonymous, issues | |||
|
230 | end | |||
|
231 | ||||
222 | def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default |
|
232 | def test_anonymous_should_not_see_private_issues_with_issues_visibility_set_to_default | |
223 | assert Role.anonymous.update_attribute(:issues_visibility, 'default') |
|
233 | assert Role.anonymous.update_attribute(:issues_visibility, 'default') | |
224 | issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true) |
|
234 | issue = Issue.generate!(:author => User.anonymous, :assigned_to => User.anonymous, :is_private => true) | |
@@ -265,6 +275,17 class IssueTest < ActiveSupport::TestCase | |||||
265 | assert_visibility_match user, issues |
|
275 | assert_visibility_match user, issues | |
266 | end |
|
276 | end | |
267 |
|
277 | |||
|
278 | def test_visible_scope_for_non_member_without_view_issues_permissions_and_membership | |||
|
279 | Role.non_member.remove_permission!(:view_issues) | |||
|
280 | Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [2]) | |||
|
281 | user = User.find(9) | |||
|
282 | ||||
|
283 | issues = Issue.visible(user).all | |||
|
284 | assert issues.any? | |||
|
285 | assert_equal [1], issues.map(&:project_id).uniq.sort | |||
|
286 | assert_visibility_match user, issues | |||
|
287 | end | |||
|
288 | ||||
268 | def test_visible_scope_for_member |
|
289 | def test_visible_scope_for_member | |
269 | user = User.find(9) |
|
290 | user = User.find(9) | |
270 | # User should see issues of projects for which user has view_issues permissions only |
|
291 | # User should see issues of projects for which user has view_issues permissions only | |
@@ -1724,6 +1745,16 class IssueTest < ActiveSupport::TestCase | |||||
1724 | end |
|
1745 | end | |
1725 | end |
|
1746 | end | |
1726 |
|
1747 | |||
|
1748 | def test_assignable_users_should_not_include_builtin_groups | |||
|
1749 | Member.create!(:project_id => 1, :principal => Group.non_member, :role_ids => [1]) | |||
|
1750 | Member.create!(:project_id => 1, :principal => Group.anonymous, :role_ids => [1]) | |||
|
1751 | issue = Issue.new(:project => Project.find(1)) | |||
|
1752 | ||||
|
1753 | with_settings :issue_group_assignment => '1' do | |||
|
1754 | assert_nil issue.assignable_users.detect {|u| u.is_a?(GroupBuiltin)} | |||
|
1755 | end | |||
|
1756 | end | |||
|
1757 | ||||
1727 | def test_create_should_send_email_notification |
|
1758 | def test_create_should_send_email_notification | |
1728 | ActionMailer::Base.deliveries.clear |
|
1759 | ActionMailer::Base.deliveries.clear | |
1729 | issue = Issue.new(:project_id => 1, :tracker_id => 1, |
|
1760 | issue = Issue.new(:project_id => 1, :tracker_id => 1, |
@@ -55,17 +55,11 class PrincipalTest < ActiveSupport::TestCase | |||||
55 | end |
|
55 | end | |
56 |
|
56 | |||
57 | def test_sorted_scope_should_sort_users_before_groups |
|
57 | def test_sorted_scope_should_sort_users_before_groups | |
58 |
scope = Principal.where( |
|
58 | scope = Principal.where(:type => ['User', 'Group']) | |
59 | expected_order = scope.all.sort do |a, b| |
|
59 | users = scope.select {|p| p.is_a?(User)}.sort | |
60 | if a.is_a?(User) && b.is_a?(Group) |
|
60 | groups = scope.select {|p| p.is_a?(Group)}.sort | |
61 | -1 |
|
61 | ||
62 | elsif a.is_a?(Group) && b.is_a?(User) |
|
62 | assert_equal (users + groups).map(&:name).map(&:downcase), | |
63 | 1 |
|
|||
64 | else |
|
|||
65 | a.name.downcase <=> b.name.downcase |
|
|||
66 | end |
|
|||
67 | end |
|
|||
68 | assert_equal expected_order.map(&:name).map(&:downcase), |
|
|||
69 | scope.sorted.map(&:name).map(&:downcase) |
|
63 | scope.sorted.map(&:name).map(&:downcase) | |
70 | end |
|
64 | end | |
71 |
|
65 |
@@ -1240,7 +1240,7 class QueryTest < ActiveSupport::TestCase | |||||
1240 | assert query.available_filters.keys.include?("member_of_group") |
|
1240 | assert query.available_filters.keys.include?("member_of_group") | |
1241 | assert_equal :list_optional, query.available_filters["member_of_group"][:type] |
|
1241 | assert_equal :list_optional, query.available_filters["member_of_group"][:type] | |
1242 | assert query.available_filters["member_of_group"][:values].present? |
|
1242 | assert query.available_filters["member_of_group"][:values].present? | |
1243 |
assert_equal Group. |
|
1243 | assert_equal Group.givable.sort.map {|g| [g.name, g.id.to_s]}, | |
1244 | query.available_filters["member_of_group"][:values].sort |
|
1244 | query.available_filters["member_of_group"][:values].sort | |
1245 | end |
|
1245 | end | |
1246 |
|
1246 |
@@ -838,14 +838,42 class UserTest < ActiveSupport::TestCase | |||||
838 | assert_nil membership |
|
838 | assert_nil membership | |
839 | end |
|
839 | end | |
840 |
|
840 | |||
841 | def test_roles_for_project |
|
841 | def test_roles_for_project_with_member_on_public_project_should_return_roles_and_non_member | |
842 | # user with a role |
|
|||
843 | roles = @jsmith.roles_for_project(Project.find(1)) |
|
842 | roles = @jsmith.roles_for_project(Project.find(1)) | |
844 | assert_kind_of Role, roles.first |
|
843 | assert_kind_of Role, roles.first | |
845 |
assert_equal "Manager", roles. |
|
844 | assert_equal ["Manager"], roles.map(&:name) | |
|
845 | end | |||
|
846 | ||||
|
847 | def test_roles_for_project_with_member_on_private_project_should_return_roles | |||
|
848 | Project.find(1).update_attribute :is_public, false | |||
|
849 | ||||
|
850 | roles = @jsmith.roles_for_project(Project.find(1)) | |||
|
851 | assert_kind_of Role, roles.first | |||
|
852 | assert_equal ["Manager"], roles.map(&:name) | |||
|
853 | end | |||
|
854 | ||||
|
855 | def test_roles_for_project_with_non_member_with_public_project_should_return_non_member | |||
|
856 | roles = User.find(8).roles_for_project(Project.find(1)) | |||
|
857 | assert_equal ["Non member"], roles.map(&:name) | |||
|
858 | end | |||
|
859 | ||||
|
860 | def test_roles_for_project_with_non_member_with_public_project_should_return_no_roles | |||
|
861 | Project.find(1).update_attribute :is_public, false | |||
|
862 | ||||
|
863 | roles = User.find(8).roles_for_project(Project.find(1)) | |||
|
864 | assert_equal [], roles.map(&:name) | |||
|
865 | end | |||
|
866 | ||||
|
867 | def test_roles_for_project_with_anonymous_with_public_project_should_return_anonymous | |||
|
868 | roles = User.anonymous.roles_for_project(Project.find(1)) | |||
|
869 | assert_equal ["Anonymous"], roles.map(&:name) | |||
|
870 | end | |||
846 |
|
871 | |||
847 | # user with no role |
|
872 | def test_roles_for_project_with_anonymous_with_public_project_should_return_no_roles | |
848 | assert_nil @dlopper.roles_for_project(Project.find(2)).detect {|role| role.member?} |
|
873 | Project.find(1).update_attribute :is_public, false | |
|
874 | ||||
|
875 | roles = User.anonymous.roles_for_project(Project.find(1)) | |||
|
876 | assert_equal [], roles.map(&:name) | |||
849 | end |
|
877 | end | |
850 |
|
878 | |||
851 | def test_projects_by_role_for_user_with_role |
|
879 | def test_projects_by_role_for_user_with_role |
General Comments 0
You need to be logged in to leave comments.
Login now