diff --git a/CHANGELOG b/CHANGELOG index 7bc561b6e0..dd37ab0c1c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -68,6 +68,7 @@ v 7.9.0 (unreleased) - Execute hooks and services when branch or tag is created or deleted through web interface. - Block and unblock user if he/she was blocked/unblocked in Active Directory - Raise recommended number of unicorn workers from 2 to 3 + - Use same layout and interactivity for project members as group members. v 7.8.4 - Fix issue_tracker_id substitution in custom issue trackers diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index e1015a63d5..edf482f33d 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -73,9 +73,12 @@ class Dispatcher new Activities() shortcut_handler = new ShortcutsNavigation() new ProjectsList() - when 'groups:members' + when 'groups:group_members:index' new GroupMembers() new UsersSelect() + when 'projects:project_members:index' + new ProjectMembers() + new UsersSelect() when 'groups:new', 'groups:edit', 'admin:groups:edit' new GroupAvatar() when 'projects:tree:show' @@ -127,9 +130,8 @@ class Dispatcher new DropzoneInput($('.wiki-form')) when 'snippets', 'labels', 'graphs' shortcut_handler = new ShortcutsNavigation() - when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' + when 'project_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' shortcut_handler = new ShortcutsNavigation() - new UsersSelect() # If we haven't installed a custom shortcut handler, install the default one diff --git a/app/assets/javascripts/project_members.js.coffee b/app/assets/javascripts/project_members.js.coffee new file mode 100644 index 0000000000..896ba7e53e --- /dev/null +++ b/app/assets/javascripts/project_members.js.coffee @@ -0,0 +1,4 @@ +class @ProjectMembers + constructor: -> + $('li.project_member').bind 'ajax:success', -> + $(this).fadeOut() diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index af8e90eb1a..876eea72e8 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -167,7 +167,7 @@ li.note { background-color: inherit; } -.team_member_show { +.project_member_show { td:first-child { color: #aaa; } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index e359aa4502..7475595167 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -156,7 +156,7 @@ ul.nav.nav-projects-tabs { } } -.team_member_row form { +.project_member_row form { margin: 0px; } diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index e338abeac4..9d9adaa467 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -1,5 +1,5 @@ class Admin::GroupsController < Admin::ApplicationController - before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :project_teams_update] + before_filter :group, only: [:edit, :show, :update, :destroy, :project_update, :members_update] def index @groups = Group.all @@ -40,7 +40,7 @@ class Admin::GroupsController < Admin::ApplicationController end end - def project_teams_update + def members_update @group.add_users(params[:user_ids].split(','), params[:access_level]) redirect_to [:admin, @group], notice: 'Users were successfully added.' diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb index b827639978..ed14f4e1f3 100644 --- a/app/controllers/dashboard/groups_controller.rb +++ b/app/controllers/dashboard/groups_controller.rb @@ -1,21 +1,5 @@ class Dashboard::GroupsController < ApplicationController def index - @user_groups = current_user.group_members.page(params[:page]).per(PER_PAGE) - end - - def leave - @users_group = group.group_members.where(user_id: current_user.id).first - if can?(current_user, :destroy, @users_group) - @users_group.destroy - redirect_to(dashboard_groups_path, info: "You left #{group.name} group.") - else - return render_403 - end - end - - private - - def group - @group ||= Group.find_by(path: params[:id]) + @group_members = current_user.group_members.page(params[:page]).per(PER_PAGE) end end diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index 7f27f2bb73..a73b8fa212 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -2,9 +2,27 @@ class Groups::ApplicationController < ApplicationController private + def authorize_read_group! + unless @group and can?(current_user, :read_group, @group) + if current_user.nil? + return authenticate_user! + else + return render_404 + end + end + end + def authorize_admin_group! unless can?(current_user, :manage_group, group) return render_404 end end + + def determine_layout + if current_user + 'group' + else + 'public_group' + end + end end diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index b083cf5d8c..2df51c97a2 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -1,15 +1,30 @@ class Groups::GroupMembersController < Groups::ApplicationController + skip_before_filter :authenticate_user!, only: [:index] before_filter :group # Authorize - before_filter :authorize_admin_group! + before_filter :authorize_read_group! + before_filter :authorize_admin_group!, except: [:index, :leave] - layout 'group' + layout :determine_layout + + def index + @project = @group.projects.find(params[:project_id]) if params[:project_id] + @members = @group.group_members + + if params[:search].present? + users = @group.users.search(params[:search]).to_a + @members = @members.where(user_id: users) + end + + @members = @members.order('access_level DESC').page(params[:page]).per(50) + @group_member = GroupMember.new + end def create @group.add_users(params[:user_ids].split(','), params[:access_level]) - redirect_to members_group_path(@group), notice: 'Users were successfully added.' + redirect_to group_group_members_path(@group), notice: 'Users were successfully added.' end def update @@ -18,12 +33,12 @@ class Groups::GroupMembersController < Groups::ApplicationController end def destroy - @users_group = @group.group_members.find(params[:id]) + @group_member = @group.group_members.find(params[:id]) - if can?(current_user, :destroy, @users_group) # May fail if last owner. - @users_group.destroy + if can?(current_user, :destroy_group_member, @group_member) # May fail if last owner. + @group_member.destroy respond_to do |format| - format.html { redirect_to members_group_path(@group), notice: 'User was successfully removed from group.' } + format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' } format.js { render nothing: true } end else @@ -31,6 +46,17 @@ class Groups::GroupMembersController < Groups::ApplicationController end end + def leave + @group_member = @group.group_members.where(user_id: current_user.id).first + + if can?(current_user, :destroy_group_member, @group_member) + @group_member.destroy + redirect_to(dashboard_groups_path, info: "You left #{group.name} group.") + else + return render_403 + end + end + protected def group diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 7e336803fb..7af3c07718 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,5 +1,5 @@ class GroupsController < Groups::ApplicationController - skip_before_filter :authenticate_user!, only: [:show, :issues, :members, :merge_requests] + skip_before_filter :authenticate_user!, only: [:show, :issues, :merge_requests] respond_to :html before_filter :group, except: [:new, :create] @@ -67,19 +67,6 @@ class GroupsController < Groups::ApplicationController end end - def members - @project = group.projects.find(params[:project_id]) if params[:project_id] - @members = group.group_members - - if params[:search].present? - users = group.users.search(params[:search]).to_a - @members = @members.where(user_id: users) - end - - @members = @members.order('access_level DESC').page(params[:page]).per(50) - @users_group = GroupMember.new - end - def edit end diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb index 433c19189a..3fdcbbab61 100644 --- a/app/controllers/profiles/notifications_controller.rb +++ b/app/controllers/profiles/notifications_controller.rb @@ -14,9 +14,9 @@ class Profiles::NotificationsController < ApplicationController @saved = if type == 'global' current_user.update_attributes(user_params) elsif type == 'group' - users_group = current_user.group_members.find(params[:notification_id]) - users_group.notification_level = params[:notification_level] - users_group.save + group_member = current_user.group_members.find(params[:notification_id]) + group_member.notification_level = params[:notification_level] + group_member.save else project_member = current_user.project_members.find(params[:notification_id]) project_member.notification_level = params[:notification_level] diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb new file mode 100644 index 0000000000..4ab15db01f --- /dev/null +++ b/app/controllers/projects/project_members_controller.rb @@ -0,0 +1,88 @@ +class Projects::ProjectMembersController < Projects::ApplicationController + # Authorize + before_filter :authorize_admin_project!, except: :leave + + layout "project_settings" + + def index + @project_members = @project.project_members + + if params[:search].present? + users = @project.users.search(params[:search]).to_a + @project_members = @project_members.where(user_id: users) + end + + @project_members = @project_members.order('access_level DESC') + + @group = @project.group + if @group + @group_members = @group.group_members + + if params[:search].present? + users = @group.users.search(params[:search]).to_a + @group_members = @group_members.where(user_id: users) + end + + @group_members = @group_members.order('access_level DESC').limit(20) + end + + @project_member = @project.project_members.new + end + + def new + @project_member = @project.project_members.new + end + + def create + users = User.where(id: params[:user_ids].split(',')) + @project.team << [users, params[:access_level]] + + redirect_to namespace_project_project_members_path(@project.namespace, @project) + end + + def update + @project_member = @project.project_members.find_by(user_id: member) + @project_member.update_attributes(member_params) + end + + def destroy + @project_member = @project.project_members.find_by(user_id: member) + @project_member.destroy + + respond_to do |format| + format.html do + redirect_to namespace_project_project_members_path(@project.namespace, + @project) + end + format.js { render nothing: true } + end + end + + def leave + @project.project_members.find_by(user_id: current_user).destroy + + respond_to do |format| + format.html { redirect_to :back } + format.js { render nothing: true } + end + end + + def apply_import + giver = Project.find(params[:source_project_id]) + status = @project.team.import(giver) + notice = status ? "Successfully imported" : "Import failed" + + redirect_to(namespace_project_project_members_path(project.namespace, project), + notice: notice) + end + + protected + + def member + @member ||= User.find_by(username: params[:id]) + end + + def member_params + params.require(:project_member).permit(:user_id, :access_level) + end +end diff --git a/app/controllers/projects/team_members_controller.rb b/app/controllers/projects/team_members_controller.rb deleted file mode 100644 index f8a248ed72..0000000000 --- a/app/controllers/projects/team_members_controller.rb +++ /dev/null @@ -1,73 +0,0 @@ -class Projects::TeamMembersController < Projects::ApplicationController - # Authorize - before_filter :authorize_admin_project!, except: :leave - - layout "project_settings" - - def index - @group = @project.group - @project_members = @project.project_members.order('access_level DESC') - end - - def new - @user_project_relation = @project.project_members.new - end - - def create - users = User.where(id: params[:user_ids].split(',')) - @project.team << [users, params[:access_level]] - - redirect_to namespace_project_team_index_path(@project.namespace, @project) - end - - def update - @user_project_relation = @project.project_members.find_by(user_id: member) - @user_project_relation.update_attributes(member_params) - - unless @user_project_relation.valid? - flash[:alert] = "User should have at least one role" - end - redirect_to namespace_project_team_index_path(@project.namespace, @project) - end - - def destroy - @user_project_relation = @project.project_members.find_by(user_id: member) - @user_project_relation.destroy - - respond_to do |format| - format.html do - redirect_to namespace_project_team_index_path(@project.namespace, - @project) - end - format.js { render nothing: true } - end - end - - def leave - @project.project_members.find_by(user_id: current_user).destroy - - respond_to do |format| - format.html { redirect_to :back } - format.js { render nothing: true } - end - end - - def apply_import - giver = Project.find(params[:source_project_id]) - status = @project.team.import(giver) - notice = status ? "Successfully imported" : "Import failed" - - redirect_to(namespace_project_team_index_path(project.namespace, project), - notice: notice) - end - - protected - - def member - @member ||= User.find_by(username: params[:id]) - end - - def member_params - params.require(:project_member).permit(:user_id, :access_level) - end -end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index cb82903769..7d3fcfa703 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -60,7 +60,7 @@ module SearchHelper { label: "#{prefix} - Merge Requests", url: namespace_project_merge_requests_path(@project.namespace, @project) }, { label: "#{prefix} - Milestones", url: namespace_project_milestones_path(@project.namespace, @project) }, { label: "#{prefix} - Snippets", url: namespace_project_snippets_path(@project.namespace, @project) }, - { label: "#{prefix} - Team", url: namespace_project_team_index_path(@project.namespace, @project) }, + { label: "#{prefix} - Members", url: namespace_project_project_members_path(@project.namespace, @project) }, { label: "#{prefix} - Wiki", url: namespace_project_wikis_path(@project.namespace, @project) }, ] else diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index 7a401a274d..a1d263d9d3 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -89,7 +89,7 @@ module TabHelper def project_tab_class return "active" if current_page?(controller: "/projects", action: :edit, id: @project) - if ['services', 'hooks', 'deploy_keys', 'team_members', 'protected_branches'].include? controller.controller_name + if ['services', 'hooks', 'deploy_keys', 'project_members', 'protected_branches'].include? controller.controller_name "active" end end diff --git a/app/mailers/emails/groups.rb b/app/mailers/emails/groups.rb index 8c09389985..26f43bf955 100644 --- a/app/mailers/emails/groups.rb +++ b/app/mailers/emails/groups.rb @@ -1,10 +1,10 @@ module Emails module Groups - def group_access_granted_email(user_group_id) - @membership = GroupMember.find(user_group_id) - @group = @membership.group + def group_access_granted_email(group_member_id) + @group_member = GroupMember.find(group_member_id) + @group = @group_member.group @target_url = group_url(@group) - mail(to: @membership.user.email, + mail(to: @group_member.user.email, subject: subject("Access to group was granted")) end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 890417e780..855134dd39 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -14,7 +14,7 @@ class Ability when "MergeRequest" then merge_request_abilities(user, subject) when "Group" then group_abilities(user, subject) when "Namespace" then namespace_abilities(user, subject) - when "GroupMember" then users_group_abilities(user, subject) + when "GroupMember" then group_member_abilities(user, subject) else [] end.concat(global_abilities(user)) end @@ -37,7 +37,7 @@ class Ability :read_issue, :read_milestone, :read_project_snippet, - :read_team_member, + :read_project_member, :read_merge_request, :read_note, :download_code @@ -119,7 +119,7 @@ class Ability :read_issue, :read_milestone, :read_project_snippet, - :read_team_member, + :read_project_member, :read_merge_request, :read_note, :write_project, @@ -166,7 +166,7 @@ class Ability :admin_issue, :admin_milestone, :admin_project_snippet, - :admin_team_member, + :admin_project_member, :admin_merge_request, :admin_note, :admin_wiki, @@ -248,17 +248,17 @@ class Ability end end - def users_group_abilities(user, subject) + def group_member_abilities(user, subject) rules = [] target_user = subject.user group = subject.group can_manage = group_abilities(user, group).include?(:manage_group) if can_manage && (user != target_user) - rules << :modify - rules << :destroy + rules << :modify_group_member + rules << :destroy_group_member end if !group.last_owner?(user) && (can_manage || (user == target_user)) - rules << :destroy + rules << :destroy_group_member end rules end diff --git a/app/models/issue.rb b/app/models/issue.rb index 19e43ebd78..6e10205138 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -32,7 +32,6 @@ class Issue < ActiveRecord::Base validates :project, presence: true scope :of_group, ->(group) { where(project_id: group.project_ids) } - scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } scope :cared, ->(user) { where(assignee_id: user) } scope :open_for, ->(user) { opened.assigned_to(user) } diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb index e4791d0f0a..6b13e0ff30 100644 --- a/app/models/members/project_member.rb +++ b/app/models/members/project_member.rb @@ -116,14 +116,14 @@ class ProjectMember < Member def post_create_hook unless owner? event_service.join_project(self.project, self.user) - notification_service.new_team_member(self) + notification_service.new_project_member(self) end system_hook_service.execute_hooks_for(self, :create) end def post_update_hook - notification_service.update_team_member(self) if self.access_level_changed? + notification_service.update_project_member(self) if self.access_level_changed? end def post_destroy_hook diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index f758126cfe..4cbdc61229 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -115,7 +115,6 @@ class MergeRequest < ActiveRecord::Base validate :validate_fork scope :of_group, ->(group) { where("source_project_id in (:group_project_ids) OR target_project_id in (:group_project_ids)", group_project_ids: group.project_ids) } - scope :of_user_team, ->(team) { where("(source_project_id in (:team_project_ids) OR target_project_id in (:team_project_ids) AND assignee_id in (:team_member_ids))", team_project_ids: team.project_ids, team_member_ids: team.member_ids) } scope :merged, -> { with_state(:merged) } scope :by_branch, ->(branch_name) { where("(source_branch LIKE :branch) OR (target_branch LIKE :branch)", branch: branch_name) } scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) } diff --git a/app/models/project.rb b/app/models/project.rb index c45338bf4e..1d1ae569fc 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -157,7 +157,6 @@ class Project < ActiveRecord::Base scope :without_user, ->(user) { where('projects.id NOT IN (:ids)', ids: user.authorized_projects.map(&:id) ) } scope :without_team, ->(team) { team.projects.present? ? where('projects.id NOT IN (:ids)', ids: team.projects.map(&:id)) : scoped } scope :not_in_group, ->(group) { where('projects.id NOT IN (:ids)', ids: group.project_ids ) } - scope :in_team, ->(team) { where('projects.id IN (:ids)', ids: team.projects.map(&:id)) } scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) } scope :in_group_namespace, -> { joins(:group) } scope :personal, ->(user) { where(namespace_id: user.namespace_id) } @@ -445,13 +444,13 @@ class Project < ActiveRecord::Base end end - def team_member_by_name_or_email(name = nil, email = nil) + def project_member_by_name_or_email(name = nil, email = nil) user = users.where('name like ? or email like ?', name, email).first project_members.where(user: user) if user end # Get Team Member record by user id - def team_member_by_id(user_id) + def project_member_by_id(user_id) project_members.find_by(user_id: user_id) end diff --git a/app/models/project_team.rb b/app/models/project_team.rb index bc9c3ce58f..d4a07caf9e 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -31,16 +31,16 @@ class ProjectTeam user end - def find_tm(user_id) - tm = project.project_members.find_by(user_id: user_id) + def find_member(user_id) + member = project.project_members.find_by(user_id: user_id) # If user is not in project members # we should check for group membership - if group && !tm - tm = group.group_members.find_by(user_id: user_id) + if group && !member + member = group.group_members.find_by(user_id: user_id) end - tm + member end def add_user(user, access) @@ -91,24 +91,24 @@ class ProjectTeam def import(source_project) target_project = project - source_team = source_project.project_members.to_a + source_members = source_project.project_members.to_a target_user_ids = target_project.project_members.pluck(:user_id) - source_team.reject! do |tm| + source_members.reject! do |member| # Skip if user already present in team - target_user_ids.include?(tm.user_id) + target_user_ids.include?(member.user_id) end - source_team.map! do |tm| - new_tm = tm.dup - new_tm.id = nil - new_tm.source = target_project - new_tm + source_members.map! do |member| + new_member = member.dup + new_member.id = nil + new_member.source = target_project + new_member end ProjectMember.transaction do - source_team.each do |tm| - tm.save + source_members.each do |member| + member.save end end @@ -118,26 +118,26 @@ class ProjectTeam end def guest?(user) - max_tm_access(user.id) == Gitlab::Access::GUEST + max_member_access(user.id) == Gitlab::Access::GUEST end def reporter?(user) - max_tm_access(user.id) == Gitlab::Access::REPORTER + max_member_access(user.id) == Gitlab::Access::REPORTER end def developer?(user) - max_tm_access(user.id) == Gitlab::Access::DEVELOPER + max_member_access(user.id) == Gitlab::Access::DEVELOPER end def master?(user) - max_tm_access(user.id) == Gitlab::Access::MASTER + max_member_access(user.id) == Gitlab::Access::MASTER end def member?(user_id) - !!find_tm(user_id) + !!find_member(user_id) end - def max_tm_access(user_id) + def max_member_access(user_id) access = [] access << project.project_members.find_by(user_id: user_id).try(:access_field) diff --git a/app/models/user.rb b/app/models/user.rb index 0d40ac8309..ba325132df 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -169,11 +169,8 @@ class User < ActiveRecord::Base scope :admins, -> { where(admin: true) } scope :blocked, -> { with_state(:blocked) } scope :active, -> { with_state(:active) } - scope :in_team, ->(team){ where(id: team.member_ids) } - scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) } scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') } - scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active } # # Class methods @@ -407,7 +404,7 @@ class User < ActiveRecord::Base end def tm_of(project) - project.team_member_by_id(self.id) + project.project_member_by_id(self.id) end def already_forked?(project) diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 0063b7ce40..fb411c3e23 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -162,20 +162,20 @@ class NotificationService end end - def new_team_member(project_member) + def new_project_member(project_member) mailer.project_access_granted_email(project_member.id) end - def update_team_member(project_member) + def update_project_member(project_member) mailer.project_access_granted_email(project_member.id) end - def new_group_member(users_group) - mailer.group_access_granted_email(users_group.id) + def new_group_member(group_member) + mailer.group_access_granted_email(group_member.id) end - def update_group_member(users_group) - mailer.group_access_granted_email(users_group.id) + def update_group_member(group_member) + mailer.group_access_granted_email(group_member.id) end def project_was_moved(project) @@ -194,11 +194,11 @@ class NotificationService project_members = project_member_notification(project) users_with_project_level_global = project_member_notification(project, Notification::N_GLOBAL) - users_with_group_level_global = users_group_notification(project, Notification::N_GLOBAL) + users_with_group_level_global = group_member_notification(project, Notification::N_GLOBAL) users = users_with_global_level_watch([users_with_project_level_global, users_with_group_level_global].flatten.uniq) users_with_project_setting = select_project_member_setting(project, users_with_project_level_global, users) - users_with_group_setting = select_users_group_setting(project, project_members, users_with_group_level_global, users) + users_with_group_setting = select_group_member_setting(project, project_members, users_with_group_level_global, users) User.where(id: users_with_project_setting.concat(users_with_group_setting).uniq).to_a end @@ -213,7 +213,7 @@ class NotificationService end end - def users_group_notification(project, notification_level) + def group_member_notification(project, notification_level) if project.group project.group.group_members.where(notification_level: notification_level).pluck(:user_id) else @@ -243,8 +243,8 @@ class NotificationService end # Build a list of users based on group notification settings - def select_users_group_setting(project, project_members, global_setting, users_global_level_watch) - uids = users_group_notification(project, Notification::N_WATCH) + def select_group_member_setting(project, project_members, global_setting, users_global_level_watch) + uids = group_member_notification(project, Notification::N_WATCH) # Group setting is watch, add to users list if user is not project member users = [] @@ -273,20 +273,20 @@ class NotificationService users.reject do |user| next user.notification.disabled? unless project - tm = project.project_members.find_by(user_id: user.id) + member = project.project_members.find_by(user_id: user.id) - if !tm && project.group - tm = project.group.group_members.find_by(user_id: user.id) + if !member && project.group + member = project.group.group_members.find_by(user_id: user.id) end # reject users who globally disabled notification and has no membership - next user.notification.disabled? unless tm + next user.notification.disabled? unless member # reject users who disabled notification in project - next true if tm.notification.disabled? + next true if member.notification.disabled? # reject users who have N_GLOBAL in project and disabled in global settings - tm.notification.global? && user.notification.disabled? + member.notification.global? && user.notification.disabled? end end @@ -297,20 +297,20 @@ class NotificationService users.reject do |user| next user.notification.mention? unless project - tm = project.project_members.find_by(user_id: user.id) + member = project.project_members.find_by(user_id: user.id) - if !tm && project.group - tm = project.group.group_members.find_by(user_id: user.id) + if !member && project.group + member = project.group.group_members.find_by(user_id: user.id) end # reject users who globally set mention notification and has no membership - next user.notification.mention? unless tm + next user.notification.mention? unless member # reject users who set mention notification in project - next true if tm.notification.mention? + next true if member.notification.mention? # reject users who have N_MENTION in project and disabled in global settings - tm.notification.global? && user.notification.mention? + member.notification.global? && user.notification.mention? end end diff --git a/app/services/projects/participants_service.rb b/app/services/projects/participants_service.rb index f6f9aceef9..bcbacbff56 100644 --- a/app/services/projects/participants_service.rb +++ b/app/services/projects/participants_service.rb @@ -12,8 +12,8 @@ module Projects else [] end - team_members = sorted(@project.team.members) - participants = all_members + groups + team_members + participating + project_members = sorted(@project.team.members) + participants = all_members + groups + project_members + participating participants.uniq end diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml index a28eae3892..7d29211807 100644 --- a/app/views/admin/groups/show.html.haml +++ b/app/views/admin/groups/show.html.haml @@ -58,7 +58,7 @@ Read more about project permissions %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" - = form_tag project_teams_update_admin_group_path(@group), id: "new_team_member", class: "bulk_import", method: :put do + = form_tag members_update_admin_group_path(@group), id: "new_project_member", class: "bulk_import", method: :put do %div = users_select_tag(:user_ids, multiple: true) %div.prepend-top-10 diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index ebb3b3a636..077ee56908 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -111,10 +111,10 @@ %small (#{@project.users.count}) .pull-right - = link_to namespace_project_team_index_path(@project.namespace, @project), class: "btn btn-xs" do + = link_to namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-xs" do %i.fa.fa-pencil-square-o Manage Access - %ul.well-list.team_members + %ul.well-list.project_members - @project_members.each do |project_member| - user = project_member.user %li.project_member @@ -126,7 +126,7 @@ %span.light Owner - else %span.light= project_member.human_access - = link_to namespace_project_team_member_path(@project.namespace, @project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, remote: true, class: "btn btn-sm btn-remove" do + = link_to namespace_project_project_member_path(@project.namespace, @project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, remote: true, class: "btn btn-sm btn-remove" do %i.fa.fa-times .panel-footer = paginate @project_members, param_name: 'project_members_page', theme: 'gitlab' diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 5cf423ead8..0a2934d3bd 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -174,15 +174,15 @@ .panel.panel-default .panel-heading Groups: %ul.well-list - - @user.group_members.each do |user_group| - - group = user_group.group + - @user.group_members.each do |group_member| + - group = group_member.group %li.group_member - %span{class: ("list-item-name" unless user_group.owner?)} + %span{class: ("list-item-name" unless group_member.owner?)} %strong= link_to group.name, admin_group_path(group) .pull-right - %span.light= user_group.human_access - - unless user_group.owner? - = link_to group_group_member_path(group, user_group), data: { confirm: remove_user_from_group_message(group, @user) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do + %span.light= group_member.human_access + - unless group_member.owner? + = link_to group_group_member_path(group, group_member), data: { confirm: remove_user_from_group_message(group, @user) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do %i.fa.fa-times.fa-inverse - else .nothing-here-block This user has no groups. @@ -207,21 +207,21 @@ .panel-heading Joined projects (#{@joined_projects.count}) %ul.well-list - @joined_projects.sort_by(&:name_with_namespace).each do |project| - - tm = project.team.find_tm(@user.id) + - member = project.team.find_member(@user.id) %li.project_member .list-item-name = link_to admin_namespace_project_path(project.namespace, project), class: dom_class(project) do = project.name_with_namespace - - if tm + - if member .pull-right - - if tm.owner? + - if member.owner? %span.light Owner - else - %span.light= tm.human_access + %span.light= member.human_access - - if tm.respond_to? :project - = link_to namespace_project_team_member_path(project.namespace, project, @user), data: { confirm: remove_from_project_team_message(project, @user) }, remote: true, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from project' do + - if member.respond_to? :project + = link_to namespace_project_project_member_path(project.namespace, project, @user), data: { confirm: remove_from_project_team_message(project, @user) }, remote: true, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from project' do %i.fa.fa-times #ssh-keys.tab-pane = render 'profiles/keys/key_table', admin: true diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml index c232644b02..165db214d7 100644 --- a/app/views/dashboard/groups/index.html.haml +++ b/app/views/dashboard/groups/index.html.haml @@ -11,10 +11,10 @@ .panel.panel-default .panel-heading %strong Groups - (#{@user_groups.count}) + (#{@group_members.count}) %ul.well-list - - @user_groups.each do |user_group| - - group = user_group.group + - @group_members.each do |group_member| + - group = group_member.group %li .pull-right - if can?(current_user, :manage_group, group) @@ -22,8 +22,8 @@ %i.fa.fa-cogs Settings - - if can?(current_user, :destroy, user_group) - = link_to leave_dashboard_group_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-sm btn btn-grouped", title: 'Remove user from group' do + - if can?(current_user, :destroy_group_member, group_member) + = link_to leave_group_group_members_path(group), data: { confirm: leave_group_message(group.name) }, method: :delete, class: "btn-sm btn btn-grouped", title: 'Remove user from group' do %i.fa.fa-sign-out Leave @@ -32,9 +32,9 @@ %strong= group.name as - %strong #{user_group.human_access} + %strong #{group_member.human_access} %div.light #{pluralize(group.projects.count, "project")}, #{pluralize(group.users.count, "user")} -= paginate @user_groups += paginate @group_members diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml index 5bef796c5a..3d120c5cdd 100644 --- a/app/views/groups/group_members/_group_member.html.haml +++ b/app/views/groups/group_members/_group_member.html.haml @@ -1,6 +1,7 @@ - user = member.user - return unless user - show_roles = true if show_roles.nil? + %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} %span{class: ("list-item-name" if show_controls)} = image_tag avatar_icon(user.email, 16), class: "avatar s16" @@ -16,13 +17,13 @@ %span.pull-right %strong= member.human_access - if show_controls - - if can?(current_user, :modify, member) + - if can?(current_user, :modify_group_member, member) = button_tag class: "btn-xs btn js-toggle-button", title: 'Edit access level', type: 'button' do %i.fa.fa-pencil-square-o - - if can?(current_user, :destroy, member) - - if current_user == member.user - = link_to leave_dashboard_group_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from group' do + - if can?(current_user, :destroy_group_member, member) + - if current_user == user + = link_to leave_group_group_members_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from group' do %i.fa.fa-minus.fa-inverse - else = link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do diff --git a/app/views/groups/_new_group_member.html.haml b/app/views/groups/group_members/_new_group_member.html.haml similarity index 71% rename from app/views/groups/_new_group_member.html.haml rename to app/views/groups/group_members/_new_group_member.html.haml index 345c0555a3..c4c29bb2e8 100644 --- a/app/views/groups/_new_group_member.html.haml +++ b/app/views/groups/group_members/_new_group_member.html.haml @@ -1,4 +1,4 @@ -= form_for @users_group, url: group_group_members_path(@group), html: { class: 'form-horizontal users-group-form' } do |f| += form_for @group_member, url: group_group_members_path(@group), html: { class: 'form-horizontal users-group-form' } do |f| .form-group = f.label :user_ids, "People", class: 'control-label' .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large') @@ -6,7 +6,7 @@ .form-group = f.label :access_level, "Group Access", class: 'control-label' .col-sm-10 - = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @users_group.access_level), class: "project-access-select select2" + = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "project-access-select select2" .help-block Read more about role permissions %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" diff --git a/app/views/groups/members.html.haml b/app/views/groups/group_members/index.html.haml similarity index 92% rename from app/views/groups/members.html.haml rename to app/views/groups/group_members/index.html.haml index 688c22e962..0d501fe7bd 100644 --- a/app/views/groups/members.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,4 +1,5 @@ - show_roles = should_user_see_group_roles?(current_user, @group) + %h3.page-title Group members - if show_roles @@ -10,7 +11,7 @@ %hr .clearfix.js-toggle-container - = form_tag members_group_path(@group), method: :get, class: 'form-inline member-search-form' do + = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input input-mn-300' } = button_tag 'Search', class: 'btn' @@ -33,6 +34,7 @@ %ul.well-list - @members.each do |member| = render 'groups/group_members/group_member', member: member, show_roles: show_roles, show_controls: true + = paginate @members, theme: 'gitlab' :coffeescript diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 3b8c26ed39..dd1fa3840d 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -16,7 +16,7 @@ %span.label.label-gray = repository_size(project) .pull-right - = link_to 'Members', namespace_project_team_index_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" + = link_to 'Members', namespace_project_project_members_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" = link_to 'Edit', edit_namespace_project_path(project.namespace, project), id: "edit_#{dom_id(project)}", class: "btn btn-sm" = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove" - if @projects.blank? diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index ddd3df19ee..32fe0e37df 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -24,8 +24,8 @@ Merge Requests - if current_user %span.count= MergeRequest.opened.of_group(@group).count - = nav_link(path: 'groups#members') do - = link_to members_group_path(@group), title: 'Members' do + = nav_link(controller: [:group_members]) do + = link_to group_group_members_path(@group), title: 'Members' do %i.fa.fa-users %span Members diff --git a/app/views/notify/group_access_granted_email.html.haml b/app/views/notify/group_access_granted_email.html.haml index 823ebf7734..f1916d624b 100644 --- a/app/views/notify/group_access_granted_email.html.haml +++ b/app/views/notify/group_access_granted_email.html.haml @@ -1,4 +1,4 @@ %p - = "You have been granted #{@membership.human_access} access to group" + = "You have been granted #{@group_member.human_access} access to group" = link_to group_url(@group) do = @group.name diff --git a/app/views/notify/group_access_granted_email.text.erb b/app/views/notify/group_access_granted_email.text.erb index 331bb98d5c..ef9617bfc1 100644 --- a/app/views/notify/group_access_granted_email.text.erb +++ b/app/views/notify/group_access_granted_email.text.erb @@ -1,4 +1,4 @@ -You have been granted <%= @membership.human_access %> access to group <%= @group.name %> +You have been granted <%= @group_member.human_access %> access to group <%= @group.name %> <%= url_for(group_url(@group)) %> diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index 6cf5c81c19..273e72f8a4 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -62,9 +62,9 @@ By default, all projects and groups will use the notification level set above. %h4 Groups: %ul.bordered-list - - @group_members.each do |users_group| - - notification = Notification.new(users_group) - = render 'settings', type: 'group', membership: users_group, notification: notification + - @group_members.each do |group_member| + - notification = Notification.new(group_member) + = render 'settings', type: 'group', membership: group_member, notification: notification .col-md-6 %p diff --git a/app/views/projects/_dropdown.html.haml b/app/views/projects/_dropdown.html.haml index 2d5120f283..f4f4c2662c 100644 --- a/app/views/projects/_dropdown.html.haml +++ b/app/views/projects/_dropdown.html.haml @@ -15,9 +15,9 @@ %li = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do New snippet - - if can?(current_user, :admin_team_member, @project) + - if can?(current_user, :admin_project_member, @project) %li - = link_to new_namespace_project_team_member_path(@project.namespace, @project), title: "New project member" do + = link_to namespace_project_project_members_path(@project.namespace, @project), title: "New project member" do New project member - if can? current_user, :push_code, @project %li.divider diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml index 7fc3d44034..281a84a3d3 100644 --- a/app/views/projects/_settings_nav.html.haml +++ b/app/views/projects/_settings_nav.html.haml @@ -4,8 +4,8 @@ %i.fa.fa-pencil-square-o %span Project - = nav_link(controller: [:team_members, :teams]) do - = link_to namespace_project_team_index_path(@project.namespace, @project), title: 'Members', class: "team-tab tab" do + = nav_link(controller: [:project_members, :teams]) do + = link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: "team-tab tab" do %i.fa.fa-users %span Members diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml new file mode 100644 index 0000000000..b050be1d21 --- /dev/null +++ b/app/views/projects/project_members/_group_members.html.haml @@ -0,0 +1,15 @@ +.panel.panel-default + .panel-heading + %strong #{@group.name} + group members + %small + (#{members.count}) + .pull-right + = link_to group_group_members_path(@group), class: 'btn btn-sm' do + %i.fa.fa-pencil-square-o + %ul.well-list + - members.each do |member| + = render 'groups/group_members/group_member', member: member, show_controls: false + - if members.count > 20 + %li + and #{members.count - 20} more. For full list visit #{link_to 'group members page', group_group_members_path(@group)} diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml new file mode 100644 index 0000000000..0f824bdabf --- /dev/null +++ b/app/views/projects/project_members/_new_project_member.html.haml @@ -0,0 +1,15 @@ += form_for @project_member, as: :project_member, url: namespace_project_project_members_path(@project.namespace, @project), html: { class: 'form-horizontal users-project-form' } do |f| + .form-group + = f.label :user_ids, "People", class: 'control-label' + .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large') + + .form-group + = f.label :access_level, "Project Access", class: 'control-label' + .col-sm-10 + = select_tag :access_level, options_for_select(ProjectMember.access_roles, @project_member.access_level), class: "project-access-select select2" + .help-block + Read more about role permissions + %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" + + .form-actions + = f.submit 'Add users to project', class: "btn btn-create" diff --git a/app/views/projects/project_members/_project_member.html.haml b/app/views/projects/project_members/_project_member.html.haml new file mode 100644 index 0000000000..368d9419de --- /dev/null +++ b/app/views/projects/project_members/_project_member.html.haml @@ -0,0 +1,34 @@ +- user = member.user +- return unless user + +%li{class: "#{dom_class(member)} js-toggle-container project_member_row access-#{member.human_access.downcase}", id: dom_id(member)} + %span.list-item-name + = image_tag avatar_icon(user.email, 16), class: "avatar s16" + %strong= user.name + %span.cgray= user.username + - if user == current_user + %span.label.label-success It's you + - if user.blocked? + %label.label.label-danger + %strong Blocked + + - if current_user_can_admin_project + - unless @project.personal? && user == current_user + .pull-right + %strong= member.human_access + = button_tag class: "btn-xs btn js-toggle-button", + title: 'Edit access level', type: 'button' do + %i.fa.fa-pencil-square-o + + - if current_user == user + = link_to leave_namespace_project_project_members_path(@project.namespace, @project), data: { confirm: "Leave project?"}, method: :delete, class: "btn-xs btn btn-remove", title: 'Leave project' do + %i.fa.fa-minus.fa-inverse + - else + = link_to namespace_project_project_member_path(@project.namespace, @project, user), data: { confirm: remove_from_project_team_message(@project, user) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from team' do + %i.fa.fa-minus.fa-inverse + + .edit-member.hide.js-toggle-content + = form_for member, as: :project_member, url: namespace_project_project_member_path(@project.namespace, @project, member.user), remote: true do |f| + .alert.prepend-top-20 + = f.select :access_level, options_for_select(ProjectMember.access_roles, member.access_level) + = f.submit 'Save', class: 'btn btn-save btn-sm' diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml new file mode 100644 index 0000000000..615c425e59 --- /dev/null +++ b/app/views/projects/project_members/_team.html.haml @@ -0,0 +1,11 @@ +- can_admin_project = can?(current_user, :admin_project, @project) + +.panel.panel-default.prepend-top-20 + .panel-heading + %strong #{@project.name} + project members + %small + (#{members.count}) + %ul.well-list + - members.each do |project_member| + = render 'project_member', member: project_member, current_user_can_admin_project: can_admin_project diff --git a/app/views/projects/team_members/import.html.haml b/app/views/projects/project_members/import.html.haml similarity index 66% rename from app/views/projects/team_members/import.html.haml rename to app/views/projects/project_members/import.html.haml index 9e31d47117..293754cd0c 100644 --- a/app/views/projects/team_members/import.html.haml +++ b/app/views/projects/project_members/import.html.haml @@ -3,12 +3,12 @@ %p.light Only project members will be imported. Group members will be skipped. %hr -= form_tag apply_import_namespace_project_team_members_path(@project.namespace, @project), method: 'post', class: 'form-horizontal' do += form_tag apply_import_namespace_project_project_members_path(@project.namespace, @project), method: 'post', class: 'form-horizontal' do .form-group = label_tag :source_project_id, "Project", class: 'control-label' .col-sm-10= select_tag(:source_project_id, options_from_collection_for_select(current_user.authorized_projects, :id, :name_with_namespace), prompt: "Select project", class: "select2 lg", required: true) .form-actions = button_tag 'Import project members', class: "btn btn-create" - = link_to "Cancel", namespace_project_team_index_path(@project.namespace, @project), class: "btn btn-cancel" + = link_to "Cancel", namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml new file mode 100644 index 0000000000..36a6f6a155 --- /dev/null +++ b/app/views/projects/project_members/index.html.haml @@ -0,0 +1,35 @@ +%h3.page-title + Users with access to this project + +%p.light + Read more about project permissions + %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" + +%hr + +.clearfix.js-toggle-container + = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do + .form-group + = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input input-mn-300' } + = button_tag 'Search', class: 'btn' + + - if can?(current_user, :admin_project_member, @project) + %span.pull-right + = button_tag class: 'btn btn-new btn-grouped js-toggle-button', type: 'button' do + Add members + %i.fa.fa-chevron-down + = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do + Import members + + .js-toggle-content.hide.new-group-member-holder + = render "new_project_member" + += render "team", members: @project_members + +- if @group + = render "group_members", members: @group_members + +:coffeescript + $('form.member-search-form').on 'submit', (event) -> + event.preventDefault() + Turbolinks.visit @.action + '?' + $(@).serialize() diff --git a/app/views/projects/project_members/update.js.haml b/app/views/projects/project_members/update.js.haml new file mode 100644 index 0000000000..811b185882 --- /dev/null +++ b/app/views/projects/project_members/update.js.haml @@ -0,0 +1,3 @@ +- can_admin_project = can?(current_user, :admin_project, @project) +:plain + $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render("project_member", member: @project_member, current_user_can_admin_project: can_admin_project))}'); diff --git a/app/views/projects/team_members/_form.html.haml b/app/views/projects/team_members/_form.html.haml deleted file mode 100644 index 166b6362a0..0000000000 --- a/app/views/projects/team_members/_form.html.haml +++ /dev/null @@ -1,29 +0,0 @@ -%h3.page-title - New project member(s) - -= form_for @user_project_relation, as: :project_member, url: namespace_project_team_members_path(@project.namespace, @project), html: { class: "form-horizontal users-project-form" } do |f| - -if @user_project_relation.errors.any? - .alert.alert-danger - %ul - - @user_project_relation.errors.full_messages.each do |msg| - %li= msg - - %p 1. Choose people you want in the project - .form-group - = f.label :user_ids, "People", class: 'control-label' - .col-sm-10 - = users_select_tag(:user_ids, multiple: true) - - %p 2. Set access level for them - .form-group - = f.label :access_level, "Project Access", class: 'control-label' - .col-sm-10 - = select_tag :access_level, options_for_select(Gitlab::Access.options, @user_project_relation.access_level), class: "project-access-select select2" - .help-block - Read more about role permissions - %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" - - - .form-actions - = f.submit 'Add users', class: "btn btn-create" - = link_to "Cancel", namespace_project_team_index_path(@project.namespace, @project), class: "btn btn-cancel" diff --git a/app/views/projects/team_members/_group_members.html.haml b/app/views/projects/team_members/_group_members.html.haml deleted file mode 100644 index 12bd828a5e..0000000000 --- a/app/views/projects/team_members/_group_members.html.haml +++ /dev/null @@ -1,14 +0,0 @@ -- group_users_count = @group.group_members.count -.panel.panel-default - .panel-heading - %strong #{@group.name} - group members (#{group_users_count}) - .pull-right - = link_to members_group_path(@group), class: 'btn btn-sm' do - %i.fa.fa-pencil-square-o - %ul.well-list - - @group.group_members.order('access_level DESC').limit(20).each do |member| - = render 'groups/group_members/group_member', member: member, show_controls: false - - if group_users_count > 20 - %li - and #{group_users_count - 20} more. For full list visit #{link_to 'group members page', members_group_path(@group)} diff --git a/app/views/projects/team_members/_team.html.haml b/app/views/projects/team_members/_team.html.haml deleted file mode 100644 index 0e5b817613..0000000000 --- a/app/views/projects/team_members/_team.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -.team-table - - can_admin_project = (can? current_user, :admin_project, @project) - .panel.panel-default - .panel-heading - %strong #{@project.name} - project members (#{members.count}) - %ul.well-list - - members.each do |team_member| - = render 'team_member', member: team_member, current_user_can_admin_project: can_admin_project diff --git a/app/views/projects/team_members/_team_member.html.haml b/app/views/projects/team_members/_team_member.html.haml deleted file mode 100644 index 1a755bbd56..0000000000 --- a/app/views/projects/team_members/_team_member.html.haml +++ /dev/null @@ -1,18 +0,0 @@ -- user = member.user -%li{id: dom_id(user), class: "team_member_row access-#{member.human_access.downcase}"} - .pull-right - - if current_user_can_admin_project - - unless @project.personal? && user == current_user - .pull-left - = form_for(member, as: :project_member, url: namespace_project_team_member_path(@project.namespace, @project, member.user)) do |f| - = f.select :access_level, options_for_select(ProjectMember.access_roles, member.access_level), {}, class: "trigger-submit" -   - = link_to namespace_project_team_member_path(@project.namespace, @project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, class: "btn-xs btn btn-remove", title: 'Remove user from team' do - %i.fa.fa-minus.fa-inverse - = image_tag avatar_icon(user.email, 32), class: "avatar s32" - %p - %strong= user.name - - if user.blocked? - %label.label.label-danger - %strong Blocked - %span.cgray= user.username diff --git a/app/views/projects/team_members/index.html.haml b/app/views/projects/team_members/index.html.haml deleted file mode 100644 index fcc879a58d..0000000000 --- a/app/views/projects/team_members/index.html.haml +++ /dev/null @@ -1,16 +0,0 @@ -%h3.page-title - Users with access to this project - - - if can? current_user, :admin_team_member, @project - %span.pull-right - = link_to new_namespace_project_team_member_path(@project.namespace, @project), class: "btn btn-new btn-grouped", title: "New project member" do - New project member - = link_to import_namespace_project_team_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do - Import members - -%p.light - Read more about project permissions - %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" -= render "team", members: @project_members -- if @group - = render "group_members" diff --git a/app/views/projects/team_members/new.html.haml b/app/views/projects/team_members/new.html.haml deleted file mode 100644 index b1bc3ba0eb..0000000000 --- a/app/views/projects/team_members/new.html.haml +++ /dev/null @@ -1 +0,0 @@ -= render "form" diff --git a/app/views/projects/team_members/update.js.haml b/app/views/projects/team_members/update.js.haml deleted file mode 100644 index c68fe9574a..0000000000 --- a/app/views/projects/team_members/update.js.haml +++ /dev/null @@ -1,6 +0,0 @@ -- if @user_project_relation.valid? - :plain - $("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#529214"}, 1000);; -- else - :plain - $("##{dom_id(@user_project_relation)}").effect("highlight", {color: "#D12F19"}, 1000);; diff --git a/config/routes.rb b/config/routes.rb index 889995e92a..dd70ad2fa0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -136,7 +136,7 @@ Gitlab::Application.routes.draw do resources :groups, constraints: { id: /[^\/]+/ } do member do - put :project_teams_update + put :members_update end end @@ -215,11 +215,7 @@ Gitlab::Application.routes.draw do scope module: :dashboard do resources :milestones, only: [:index, :show] - resources :groups, only: [:index] do - member do - delete :leave - end - end + resources :groups, only: [:index] resources :projects, only: [] do collection do @@ -236,12 +232,14 @@ Gitlab::Application.routes.draw do member do get :issues get :merge_requests - get :members get :projects end scope module: :groups do - resources :group_members, only: [:create, :update, :destroy] + resources :group_members, only: [:index, :create, :update, :destroy] do + delete :leave, on: :collection + end + resource :avatar, only: [:destroy] resources :milestones, only: [:index, :show, :update] end @@ -425,7 +423,6 @@ Gitlab::Application.routes.draw do end end - resources :team, controller: 'team_members', only: [:index] resources :milestones, except: [:destroy], constraints: { id: /\d+/ } do member do put :sort_issues @@ -445,7 +442,7 @@ Gitlab::Application.routes.draw do end end - resources :team_members, except: [:index, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do + resources :project_members, except: [:new, :edit], constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ } do collection do delete :leave diff --git a/features/project/team_management.feature b/features/project/team_management.feature index 86ea6cd6e9..22393622bb 100644 --- a/features/project/team_management.feature +++ b/features/project/team_management.feature @@ -13,7 +13,7 @@ Feature: Project Team Management @javascript Scenario: Add user to project - Given I click link "New Team Member" + Given I click link "Add members" And I select "Mike" as "Reporter" Then I should see "Mike" in team list as "Reporter" diff --git a/features/steps/admin/groups.rb b/features/steps/admin/groups.rb index 6bcec48be8..721460b937 100644 --- a/features/steps/admin/groups.rb +++ b/features/steps/admin/groups.rb @@ -38,7 +38,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps When 'I select user "John Doe" from user list as "Reporter"' do select2(user_john.id, from: "#user_ids", multiple: true) - within "#new_team_member" do + within "#new_project_member" do select "Reporter", from: "access_level" end click_button "Add users to group" diff --git a/features/steps/explore/groups.rb b/features/steps/explore/groups.rb index ccbf6cda07..0c2127d4c4 100644 --- a/features/steps/explore/groups.rb +++ b/features/steps/explore/groups.rb @@ -35,7 +35,7 @@ class Spinach::Features::ExploreGroups < Spinach::FeatureSteps end step 'I visit group "TestGroup" members page' do - visit members_group_path(Group.find_by(name: "TestGroup")) + visit group_group_members_path(Group.find_by(name: "TestGroup")) end step 'I should not see project "Enterprise" items' do diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb index 7907f2a6fe..0eefe2b568 100644 --- a/features/steps/project/team_management.rb +++ b/features/steps/project/team_management.rb @@ -15,18 +15,18 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps page.should have_content(user.username) end - step 'I click link "New Team Member"' do - click_link "New project member" + step 'I click link "Add members"' do + find(:css, 'button.btn-new').click end step 'I select "Mike" as "Reporter"' do user = User.find_by(name: "Mike") - select2(user.id, from: "#user_ids", multiple: true) - within "#new_project_member" do + within ".users-project-form" do + select2(user.id, from: "#user_ids", multiple: true) select "Reporter", from: "access_level" end - click_button "Add users" + click_button "Add users to project" end step 'I should see "Mike" in team list as "Reporter"' do @@ -42,9 +42,13 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps end step 'I change "Sam" role to "Reporter"' do - user = User.find_by(name: "Sam") - within "#user_#{user.id}" do + project = Project.find_by(name: "Shop") + user = User.find_by(name: 'Sam') + project_member = project.project_members.find_by(user_id: user.id) + within "#project_member_#{project_member.id}" do + click_button "Edit access level" select "Reporter", from: "project_member_access_level" + click_button "Save" end end @@ -100,7 +104,10 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps end step 'I click cancel link for "Sam"' do - within "#user_#{User.find_by(name: 'Sam').id}" do + project = Project.find_by(name: "Shop") + user = User.find_by(name: 'Sam') + project_member = project.project_members.find_by(user_id: user.id) + within "#project_member_#{project_member.id}" do click_link('Remove user from team') end end diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index bb6c336d7c..e3cf1b92cd 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -32,7 +32,7 @@ module SharedPaths end step 'I visit group "Owned" members page' do - visit members_group_path(Group.find_by(name:"Owned")) + visit group_group_members_path(Group.find_by(name:"Owned")) end step 'I visit group "Owned" settings page' do @@ -52,7 +52,7 @@ module SharedPaths end step 'I visit group "Guest" members page' do - visit members_group_path(Group.find_by(name:"Guest")) + visit group_group_members_path(Group.find_by(name:"Guest")) end step 'I visit group "Guest" settings page' do @@ -386,7 +386,7 @@ module SharedPaths end step 'I visit project "Shop" team page' do - visit namespace_project_team_index_path(project.namespace, project) + visit namespace_project_project_members_path(project.namespace, project) end step 'I visit project wiki page' do diff --git a/lib/api/group_members.rb b/lib/api/group_members.rb index c9c9ccbcb2..ed54c7f6ff 100644 --- a/lib/api/group_members.rb +++ b/lib/api/group_members.rb @@ -53,14 +53,14 @@ module API authorize! :manage_group, group required_attributes! [:access_level] - team_member = group.group_members.find_by(user_id: params[:user_id]) - not_found!('User can not be found') if team_member.nil? + group_member = group.group_members.find_by(user_id: params[:user_id]) + not_found!('User can not be found') if group_member.nil? - if team_member.update_attributes(access_level: params[:access_level]) - @member = team_member.user + if group_member.update_attributes(access_level: params[:access_level]) + @member = group_member.user present @member, with: Entities::GroupMember, group: group else - handle_member_errors team_member.errors + handle_member_errors group_member.errors end end diff --git a/lib/api/project_members.rb b/lib/api/project_members.rb index 73cf062155..c756bb479f 100644 --- a/lib/api/project_members.rb +++ b/lib/api/project_members.rb @@ -46,19 +46,19 @@ module API required_attributes! [:user_id, :access_level] # either the user is already a team member or a new one - team_member = user_project.team_member_by_id(params[:user_id]) - if team_member.nil? - team_member = user_project.project_members.new( + project_member = user_project.project_member_by_id(params[:user_id]) + if project_member.nil? + project_member = user_project.project_members.new( user_id: params[:user_id], access_level: params[:access_level] ) end - if team_member.save - @member = team_member.user + if project_member.save + @member = project_member.user present @member, with: Entities::ProjectMember, project: user_project else - handle_member_errors team_member.errors + handle_member_errors project_member.errors end end @@ -74,14 +74,14 @@ module API authorize! :admin_project, user_project required_attributes! [:access_level] - team_member = user_project.project_members.find_by(user_id: params[:user_id]) - not_found!("User can not be found") if team_member.nil? + project_member = user_project.project_members.find_by(user_id: params[:user_id]) + not_found!("User can not be found") if project_member.nil? - if team_member.update_attributes(access_level: params[:access_level]) - @member = team_member.user + if project_member.update_attributes(access_level: params[:access_level]) + @member = project_member.user present @member, with: Entities::ProjectMember, project: user_project else - handle_member_errors team_member.errors + handle_member_errors project_member.errors end end @@ -94,9 +94,9 @@ module API # DELETE /projects/:id/members/:user_id delete ":id/members/:user_id" do authorize! :admin_project, user_project - team_member = user_project.project_members.find_by(user_id: params[:user_id]) - unless team_member.nil? - team_member.destroy + project_member = user_project.project_members.find_by(user_id: params[:user_id]) + unless project_member.nil? + project_member.destroy else { message: "Access revoked", id: params[:user_id].to_i } end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index 2dfa18da48..c3a8d90ef5 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -200,7 +200,7 @@ module Gitlab def reference_user(identifier, project = @project, _ = nil) options = html_options.merge( - class: "gfm gfm-team_member #{html_options[:class]}" + class: "gfm gfm-project_member #{html_options[:class]}" ) if identifier == "all" diff --git a/spec/features/security/group/group_access_spec.rb b/spec/features/security/group/group_access_spec.rb index e0c5cbf4d3..6379314945 100644 --- a/spec/features/security/group/group_access_spec.rb +++ b/spec/features/security/group/group_access_spec.rb @@ -59,8 +59,8 @@ describe "Group access", feature: true do it { is_expected.to be_denied_for :visitor } end - describe "GET /groups/:path/members" do - subject { members_group_path(group) } + describe "GET /groups/:path/group_members" do + subject { group_group_members_path(group) } it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } diff --git a/spec/features/security/group/internal_group_access_spec.rb b/spec/features/security/group/internal_group_access_spec.rb index 5279a1bc13..d17a7412e4 100644 --- a/spec/features/security/group/internal_group_access_spec.rb +++ b/spec/features/security/group/internal_group_access_spec.rb @@ -55,8 +55,8 @@ describe "Group with internal project access", feature: true do it { is_expected.to be_denied_for :visitor } end - describe "GET /groups/:path/members" do - subject { members_group_path(group) } + describe "GET /groups/:path/group_members" do + subject { group_group_members_path(group) } it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } diff --git a/spec/features/security/group/mixed_group_access_spec.rb b/spec/features/security/group/mixed_group_access_spec.rb index efd14858b9..b3db7b5dea 100644 --- a/spec/features/security/group/mixed_group_access_spec.rb +++ b/spec/features/security/group/mixed_group_access_spec.rb @@ -56,8 +56,8 @@ describe "Group access", feature: true do it { is_expected.to be_allowed_for :visitor } end - describe "GET /groups/:path/members" do - subject { members_group_path(group) } + describe "GET /groups/:path/group_members" do + subject { group_group_members_path(group) } it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } diff --git a/spec/features/security/group/public_group_access_spec.rb b/spec/features/security/group/public_group_access_spec.rb index c7e3d0a8a4..c16f0c0d1e 100644 --- a/spec/features/security/group/public_group_access_spec.rb +++ b/spec/features/security/group/public_group_access_spec.rb @@ -55,8 +55,8 @@ describe "Group with public project access", feature: true do it { is_expected.to be_allowed_for :visitor } end - describe "GET /groups/:path/members" do - subject { members_group_path(group) } + describe "GET /groups/:path/group_members" do + subject { group_group_members_path(group) } it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 322697bced..8d1bfd2522 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -79,8 +79,8 @@ describe "Internal Project Access", feature: true do it { is_expected.to be_denied_for :visitor } end - describe "GET /:project_path/team" do - subject { namespace_project_team_index_path(project.namespace, project) } + describe "GET /:project_path/project_members" do + subject { namespace_project_project_members_path(project.namespace, project) } it { is_expected.to be_allowed_for master } it { is_expected.to be_denied_for reporter } diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index ea146c3f0e..9021ff3318 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -79,8 +79,8 @@ describe "Private Project Access", feature: true do it { is_expected.to be_denied_for :visitor } end - describe "GET /:project_path/team" do - subject { namespace_project_team_index_path(project.namespace, project) } + describe "GET /:project_path/project_members" do + subject { namespace_project_project_members_path(project.namespace, project) } it { is_expected.to be_allowed_for master } it { is_expected.to be_denied_for reporter } diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index 8ee9199ff2..6ec190ed77 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -84,8 +84,8 @@ describe "Public Project Access", feature: true do it { is_expected.to be_allowed_for :visitor } end - describe "GET /:project_path/team" do - subject { namespace_project_team_index_path(project.namespace, project) } + describe "GET /:project_path/project_members" do + subject { namespace_project_project_members_path(project.namespace, project) } it { is_expected.to be_allowed_for master } it { is_expected.to be_denied_for reporter } diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index fd80c61522..6ba27b536e 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -180,7 +180,7 @@ describe GitlabMarkdownHelper do end it "should include standard gfm classes" do - expect(gfm(actual)).to match(/class="\s?gfm gfm-team_member\s?"/) + expect(gfm(actual)).to match(/class="\s?gfm gfm-project_member\s?"/) end end diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb index e04f1741b2..e206c11f33 100644 --- a/spec/models/members/group_member_spec.rb +++ b/spec/models/members/group_member_spec.rb @@ -28,18 +28,18 @@ describe GroupMember do describe "#after_update" do before do - @membership = create :group_member - @membership.stub(notification_service: double('NotificationService').as_null_object) + @group_member = create :group_member + @group_member.stub(notification_service: double('NotificationService').as_null_object) end it "should send email to user" do - expect(@membership).to receive(:notification_service) - @membership.update_attribute(:access_level, GroupMember::MASTER) + expect(@group_member).to receive(:notification_service) + @group_member.update_attribute(:access_level, GroupMember::MASTER) end it "does not send an email when the access level has not changed" do - expect(@membership).not_to receive(:notification_service) - @membership.update_attribute(:access_level, GroupMember::OWNER) + expect(@group_member).not_to receive(:notification_service) + @group_member.update_attribute(:access_level, GroupMember::OWNER) end end end diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index 4308a765b5..d9bd91f599 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -338,17 +338,14 @@ describe Projects::CommitsController, 'routing' do end end -# project_team_members GET /:project_id/team_members(.:format) team_members#index -# POST /:project_id/team_members(.:format) team_members#create -# new_project_team_member GET /:project_id/team_members/new(.:format) team_members#new -# edit_project_team_member GET /:project_id/team_members/:id/edit(.:format) team_members#edit -# project_team_member GET /:project_id/team_members/:id(.:format) team_members#show -# PUT /:project_id/team_members/:id(.:format) team_members#update -# DELETE /:project_id/team_members/:id(.:format) team_members#destroy -describe Projects::TeamMembersController, 'routing' do +# project_project_members GET /:project_id/project_members(.:format) project_members#index +# POST /:project_id/project_members(.:format) project_members#create +# PUT /:project_id/project_members/:id(.:format) project_members#update +# DELETE /:project_id/project_members/:id(.:format) project_members#destroy +describe Projects::ProjectMembersController, 'routing' do it_behaves_like 'RESTful project resources' do - let(:actions) { [:new, :create, :update, :destroy] } - let(:controller) { 'team_members' } + let(:actions) { [:index, :create, :update, :destroy] } + let(:controller) { 'project_members' } end end diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index 2074f8e7f7..34737348d4 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -69,9 +69,9 @@ describe NotificationService do user_project = note.project.project_members.find_by_user_id(@u_watcher.id) user_project.notification_level = Notification::N_PARTICIPATING user_project.save - user_group = note.project.group.group_members.find_by_user_id(@u_watcher.id) - user_group.notification_level = Notification::N_GLOBAL - user_group.save + group_member = note.project.group.group_members.find_by_user_id(@u_watcher.id) + group_member.notification_level = Notification::N_GLOBAL + group_member.save end it do