Commit 396f85e4 authored by Sean McGivern's avatar Sean McGivern

Add expiration date to group memberships

parent 8b165628
...@@ -126,6 +126,7 @@ ...@@ -126,6 +126,7 @@
new NotificationsDropdown(); new NotificationsDropdown();
break; break;
case 'groups:group_members:index': case 'groups:group_members:index':
new MemberExpirationDate();
new GroupMembers(); new GroupMembers();
new UsersSelect(); new UsersSelect();
break; break;
......
...@@ -21,7 +21,12 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -21,7 +21,12 @@ class Groups::GroupMembersController < Groups::ApplicationController
end end
def create def create
@group.add_users(params[:user_ids].split(','), params[:access_level], current_user) @group.add_users(
params[:user_ids].split(','),
params[:access_level],
current_user: current_user,
expires_at: params[:expires_at]
)
redirect_to group_group_members_path(@group), notice: 'Users were successfully added.' redirect_to group_group_members_path(@group), notice: 'Users were successfully added.'
end end
...@@ -63,7 +68,7 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -63,7 +68,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
protected protected
def member_params def member_params
params.require(:group_member).permit(:access_level, :user_id) params.require(:group_member).permit(:access_level, :user_id, :expires_at)
end end
# MembershipActions concern # MembershipActions concern
......
...@@ -95,34 +95,40 @@ class Group < Namespace ...@@ -95,34 +95,40 @@ class Group < Namespace
end end
end end
def add_users(user_ids, access_level, current_user = nil) def add_users(user_ids, access_level, current_user: nil, expires_at: nil)
user_ids.each do |user_id| user_ids.each do |user_id|
Member.add_user(self.group_members, user_id, access_level, current_user: current_user) Member.add_user(
self.group_members,
user_id,
access_level,
current_user: current_user,
expires_at: expires_at
)
end end
end end
def add_user(user, access_level, current_user = nil) def add_user(user, access_level, current_user: nil, expires_at: nil)
add_users([user], access_level, current_user) add_users([user], access_level, current_user: current_user, expires_at: expires_at)
end end
def add_guest(user, current_user = nil) def add_guest(user, current_user = nil)
add_user(user, Gitlab::Access::GUEST, current_user) add_user(user, Gitlab::Access::GUEST, current_user: current_user)
end end
def add_reporter(user, current_user = nil) def add_reporter(user, current_user = nil)
add_user(user, Gitlab::Access::REPORTER, current_user) add_user(user, Gitlab::Access::REPORTER, current_user: current_user)
end end
def add_developer(user, current_user = nil) def add_developer(user, current_user = nil)
add_user(user, Gitlab::Access::DEVELOPER, current_user) add_user(user, Gitlab::Access::DEVELOPER, current_user: current_user)
end end
def add_master(user, current_user = nil) def add_master(user, current_user = nil)
add_user(user, Gitlab::Access::MASTER, current_user) add_user(user, Gitlab::Access::MASTER, current_user: current_user)
end end
def add_owner(user, current_user = nil) def add_owner(user, current_user = nil)
add_user(user, Gitlab::Access::OWNER, current_user) add_user(user, Gitlab::Access::OWNER, current_user: current_user)
end end
def has_owner?(user) def has_owner?(user)
......
...@@ -1003,8 +1003,8 @@ class Project < ActiveRecord::Base ...@@ -1003,8 +1003,8 @@ class Project < ActiveRecord::Base
project_members.find_by(user_id: user) project_members.find_by(user_id: user)
end end
def add_user(user, access_level, current_user = nil) def add_user(user, access_level, current_user: nil, expires_at: nil)
team.add_user(user, access_level, current_user) team.add_user(user, access_level, current_user: current_user, expires_at: expires_at)
end end
def default_branch def default_branch
......
...@@ -17,7 +17,7 @@ class ProjectTeam ...@@ -17,7 +17,7 @@ class ProjectTeam
if users.respond_to?(:each) if users.respond_to?(:each)
add_users(users, access, current_user: current_user) add_users(users, access, current_user: current_user)
else else
add_user(users, access, current_user) add_user(users, access, current_user: current_user)
end end
end end
...@@ -43,8 +43,8 @@ class ProjectTeam ...@@ -43,8 +43,8 @@ class ProjectTeam
) )
end end
def add_user(user, access, current_user = nil) def add_user(user, access, current_user: nil, expires_at: nil)
add_users([user], access, current_user: current_user) add_users([user], access, current_user: current_user, expires_at: expires_at)
end end
# Remove all users from project team # Remove all users from project team
......
...@@ -14,5 +14,12 @@ ...@@ -14,5 +14,12 @@
Read more about role permissions Read more about role permissions
%strong= link_to "here", help_page_path("user/permissions"), class: "vlink" %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
.form-group
= f.label :expires_at, 'Access expiration date', class: 'control-label'
.col-sm-10
.clearable-input
= text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date'
%i.clear-icon.js-clear-input
.form-actions .form-actions
= f.submit 'Add users to group', class: "btn btn-create" = f.submit 'Add users to group', class: "btn btn-create"
:plain :plain
$("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}'); $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render('shared/members/member', member: @group_member))}');
new MemberExpirationDate();
...@@ -82,12 +82,11 @@ ...@@ -82,12 +82,11 @@
= label_tag "member_access_level_#{member.id}", 'Project access', class: 'control-label' = label_tag "member_access_level_#{member.id}", 'Project access', class: 'control-label'
.col-sm-10 .col-sm-10
= f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control', id: "member_access_level_#{member.id}" = f.select :access_level, options_for_select(member.class.access_level_roles, member.access_level), {}, class: 'form-control', id: "member_access_level_#{member.id}"
- if member.is_a?(ProjectMember) .form-group
.form-group = label_tag "member_expires_at_#{member.id}", 'Access expiration date', class: 'control-label'
= label_tag "member_expires_at_#{member.id}", 'Access expiration date', class: 'control-label' .col-sm-10
.col-sm-10 .clearable-input
.clearable-input = f.text_field :expires_at, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date', id: "member_expires_at_#{member.id}"
= f.text_field :expires_at, class: 'form-control js-access-expiration-date', placeholder: 'Select access expiration date', id: "member_expires_at_#{member.id}" %i.clear-icon.js-clear-input
%i.clear-icon.js-clear-input
.prepend-top-10 .prepend-top-10
= f.submit 'Save', class: 'btn btn-save btn-sm' = f.submit 'Save', class: 'btn btn-save btn-sm'
...@@ -117,7 +117,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps ...@@ -117,7 +117,7 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
page.within "#group_member_#{member.id}" do page.within "#group_member_#{member.id}" do
click_button 'Edit' click_button 'Edit'
select 'Developer', from: 'group_member_access_level' select 'Developer', from: "member_access_level_#{member.id}"
click_on 'Save' click_on 'Save'
end end
end end
......
...@@ -66,7 +66,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps ...@@ -66,7 +66,7 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
project_member = project.project_members.find_by(user_id: user.id) project_member = project.project_members.find_by(user_id: user.id)
page.within "#project_member_#{project_member.id}" do page.within "#project_member_#{project_member.id}" do
click_button 'Edit' click_button 'Edit'
select "Reporter", from: "project_member_access_level" select "Reporter", from: "member_access_level_#{project_member.id}"
click_button "Save" click_button "Save"
end end
end end
......
...@@ -97,6 +97,10 @@ module API ...@@ -97,6 +97,10 @@ module API
member = options[:member] || options[:members].find { |m| m.user_id == user.id } member = options[:member] || options[:members].find { |m| m.user_id == user.id }
member.access_level member.access_level
end end
expose :expires_at do |user, options|
member = options[:member] || options[:members].find { |m| m.user_id == user.id }
member.expires_at
end
end end
class AccessRequester < UserBasic class AccessRequester < UserBasic
...@@ -104,9 +108,6 @@ module API ...@@ -104,9 +108,6 @@ module API
access_requester = options[:access_requester] || options[:access_requesters].find { |m| m.user_id == user.id } access_requester = options[:access_requester] || options[:access_requesters].find { |m| m.user_id == user.id }
access_requester.requested_at access_requester.requested_at
end end
expose :expires_at do |user, options|
options[:project].project_members.find_by(user_id: user.id).expires_at
end
end end
class Group < Grape::Entity class Group < Grape::Entity
......
...@@ -49,6 +49,7 @@ module API ...@@ -49,6 +49,7 @@ module API
# id (required) - The group/project ID # id (required) - The group/project ID
# user_id (required) - The user ID of the new member # user_id (required) - The user ID of the new member
# access_level (required) - A valid access level # access_level (required) - A valid access level
# expires_at (optional) - Date string in the format YEAR-MONTH-DAY
# #
# Example Request: # Example Request:
# POST /groups/:id/members # POST /groups/:id/members
...@@ -72,7 +73,7 @@ module API ...@@ -72,7 +73,7 @@ module API
conflict!('Member already exists') if source_type == 'group' && member conflict!('Member already exists') if source_type == 'group' && member
unless member unless member
source.add_user(params[:user_id], params[:access_level], current_user) source.add_user(params[:user_id], params[:access_level], current_user: current_user, expires_at: params[:expires_at])
member = source.members.find_by(user_id: params[:user_id]) member = source.members.find_by(user_id: params[:user_id])
end end
...@@ -81,7 +82,7 @@ module API ...@@ -81,7 +82,7 @@ module API
else else
# Since `source.add_user` doesn't return a member object, we have to # Since `source.add_user` doesn't return a member object, we have to
# build a new one and populate its errors in order to render them. # build a new one and populate its errors in order to render them.
member = source.members.build(attributes_for_keys([:user_id, :access_level])) member = source.members.build(attributes_for_keys([:user_id, :access_level, :expires_at]))
member.valid? # populate the errors member.valid? # populate the errors
# This is to ensure back-compatibility but 400 behavior should be used # This is to ensure back-compatibility but 400 behavior should be used
...@@ -97,6 +98,7 @@ module API ...@@ -97,6 +98,7 @@ module API
# id (required) - The group/project ID # id (required) - The group/project ID
# user_id (required) - The user ID of the member # user_id (required) - The user ID of the member
# access_level (required) - A valid access level # access_level (required) - A valid access level
# expires_at (optional) - Date string in the format YEAR-MONTH-DAY
# #
# Example Request: # Example Request:
# PUT /groups/:id/members/:user_id # PUT /groups/:id/members/:user_id
...@@ -107,7 +109,7 @@ module API ...@@ -107,7 +109,7 @@ module API
required_attributes! [:user_id, :access_level] required_attributes! [:user_id, :access_level]
member = source.members.find_by!(user_id: params[:user_id]) member = source.members.find_by!(user_id: params[:user_id])
attrs = attributes_for_keys [:access_level] attrs = attributes_for_keys [:access_level, :expires_at]
if member.update_attributes(attrs) if member.update_attributes(attrs)
present member.user, with: Entities::Member, member: member present member.user, with: Entities::Member, member: member
......
...@@ -19,7 +19,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature: ...@@ -19,7 +19,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature:
page.within '.users-project-form' do page.within '.users-project-form' do
select2(new_member.id, from: '#user_ids', multiple: true) select2(new_member.id, from: '#user_ids', multiple: true)
fill_in 'Access expiration date', with: '2016-08-10' fill_in 'expires_at', with: '2016-08-10'
click_on 'Add users to project' click_on 'Add users to project'
end end
......
...@@ -122,12 +122,13 @@ describe API::Members, api: true do ...@@ -122,12 +122,13 @@ describe API::Members, api: true do
it 'creates a new member' do it 'creates a new member' do
expect do expect do
post api("/#{source_type.pluralize}/#{source.id}/members", master), post api("/#{source_type.pluralize}/#{source.id}/members", master),
user_id: stranger.id, access_level: Member::DEVELOPER user_id: stranger.id, access_level: Member::DEVELOPER, expires_at: '2016-08-05'
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
end.to change { source.members.count }.by(1) end.to change { source.members.count }.by(1)
expect(json_response['id']).to eq(stranger.id) expect(json_response['id']).to eq(stranger.id)
expect(json_response['access_level']).to eq(Member::DEVELOPER) expect(json_response['access_level']).to eq(Member::DEVELOPER)
expect(json_response['expires_at']).to eq('2016-08-05')
end end
end end
...@@ -183,11 +184,12 @@ describe API::Members, api: true do ...@@ -183,11 +184,12 @@ describe API::Members, api: true do
context 'when authenticated as a master/owner' do context 'when authenticated as a master/owner' do
it 'updates the member' do it 'updates the member' do
put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master), put api("/#{source_type.pluralize}/#{source.id}/members/#{developer.id}", master),
access_level: Member::MASTER access_level: Member::MASTER, expires_at: '2016-08-05'
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['id']).to eq(developer.id) expect(json_response['id']).to eq(developer.id)
expect(json_response['access_level']).to eq(Member::MASTER) expect(json_response['access_level']).to eq(Member::MASTER)
expect(json_response['expires_at']).to eq('2016-08-05')
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment