From 6d92cd3e836f2252b660479f5b33d15e6456b04d Mon Sep 17 00:00:00 2001
From: Adam Niedzielski <adamsunday@gmail.com>
Date: Mon, 1 Aug 2016 20:12:30 +0200
Subject: [PATCH] WIP

---
 .../projects/project_members_controller.rb    |  2 +-
 app/models/member.rb                          |  6 ++--
 app/models/members/project_member.rb          |  4 +--
 app/models/project_team.rb                    |  5 ++--
 app/models/user.rb                            |  2 +-
 .../_new_project_member.html.haml             |  5 ++++
 ...20160801163421_add_expires_at_to_member.rb | 29 +++++++++++++++++++
 db/schema.rb                                  |  3 +-
 8 files changed, 47 insertions(+), 9 deletions(-)
 create mode 100644 db/migrate/20160801163421_add_expires_at_to_member.rb

diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index 3435a118964..0fe96b66fa2 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -36,7 +36,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
   end
 
   def create
-    @project.team.add_users(params[:user_ids].split(','), params[:access_level], current_user)
+    @project.team.add_users(params[:user_ids].split(','), params[:access_level], current_user, params[:expires_at])
 
     redirect_to namespace_project_project_members_path(@project.namespace, @project)
   end
diff --git a/app/models/member.rb b/app/models/member.rb
index 24ab1276ee9..998144330b1 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -31,6 +31,7 @@ class Member < ActiveRecord::Base
   scope :non_invite, -> { where(invite_token: nil) }
   scope :request, -> { where.not(requested_at: nil) }
   scope :has_access, -> { where('access_level > 0') }
+  scope :still_active, -> { where('expires_at IS NULL OR expires_at > ?', Time.current) }
 
   scope :guests, -> { where(access_level: GUEST) }
   scope :reporters, -> { where(access_level: REPORTER) }
@@ -54,7 +55,7 @@ class Member < ActiveRecord::Base
 
   class << self
     def access_for_user_ids(user_ids)
-      where(user_id: user_ids).has_access.pluck(:user_id, :access_level).to_h
+      where(user_id: user_ids).has_access.still_active.pluck(:user_id, :access_level).to_h
     end
 
     def find_by_invite_token(invite_token)
@@ -73,7 +74,7 @@ class Member < ActiveRecord::Base
       user
     end
 
-    def add_user(members, user_id, access_level, current_user = nil)
+    def add_user(members, user_id, access_level, current_user = nil, expires_at = nil)
       user = user_for_id(user_id)
 
       # `user` can be either a User object or an email to be invited
@@ -87,6 +88,7 @@ class Member < ActiveRecord::Base
       if can_update_member?(current_user, member) || project_creator?(member, access_level)
         member.created_by ||= current_user
         member.access_level = access_level
+        member.expires_at = expires_at
 
         member.save
       end
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index f39afc61ce9..b86b536005b 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -33,7 +33,7 @@ class ProjectMember < Member
     #     :master
     #   )
     #
-    def add_users_into_projects(project_ids, user_ids, access, current_user = nil)
+    def add_users_into_projects(project_ids, user_ids, access, current_user = nil, expires_at = nil)
       access_level = if roles_hash.has_key?(access)
                        roles_hash[access]
                      elsif roles_hash.values.include?(access.to_i)
@@ -49,7 +49,7 @@ class ProjectMember < Member
           project = Project.find(project_id)
 
           users.each do |user|
-            Member.add_user(project.project_members, user, access_level, current_user)
+            Member.add_user(project.project_members, user, access_level, current_user, expires_at)
           end
         end
       end
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index fdfaf052730..cf1d2396974 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -33,12 +33,13 @@ class ProjectTeam
     member
   end
 
-  def add_users(users, access, current_user = nil)
+  def add_users(users, access, current_user = nil, expires_at = nil)
     ProjectMember.add_users_into_projects(
       [project.id],
       users,
       access,
-      current_user
+      current_user,
+      expires_at
     )
   end
 
diff --git a/app/models/user.rb b/app/models/user.rb
index db747434959..2995db9a5f2 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -66,7 +66,7 @@ class User < ActiveRecord::Base
   # Projects
   has_many :groups_projects,          through: :groups, source: :projects
   has_many :personal_projects,        through: :namespace, source: :projects
-  has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, class_name: 'ProjectMember'
+  has_many :project_members, -> { where(requested_at: nil).still_active }, dependent: :destroy, class_name: 'ProjectMember'
   has_many :projects,                 through: :project_members
   has_many :created_projects,         foreign_key: :creator_id, class_name: 'Project'
   has_many :users_star_projects, dependent: :destroy
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
index 978c4dfc5ec..f84aa9e5423 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -14,5 +14,10 @@
         Read more about role permissions
         %strong= link_to "here", help_page_path("user/permissions"), class: "vlink"
 
+  .form-group
+    = f.label :expires_at, "Membership expires at", class: 'control-label'
+    .col-sm-10
+      = text_field_tag :expires_at, nil, class: "datepicker form-control", placeholder: "Select expires at"
+
   .form-actions
     = f.submit 'Add users to project', class: "btn btn-create"
diff --git a/db/migrate/20160801163421_add_expires_at_to_member.rb b/db/migrate/20160801163421_add_expires_at_to_member.rb
new file mode 100644
index 00000000000..9cd37da6818
--- /dev/null
+++ b/db/migrate/20160801163421_add_expires_at_to_member.rb
@@ -0,0 +1,29 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddExpiresAtToMember < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  # Set this constant to true if this migration requires downtime.
+  DOWNTIME = false
+
+  # When a migration requires downtime you **must** uncomment the following
+  # constant and define a short and easy to understand explanation as to why the
+  # migration requires downtime.
+  # DOWNTIME_REASON = ''
+
+  # When using the methods "add_concurrent_index" or "add_column_with_default"
+  # you must disable the use of transactions as these methods can not run in an
+  # existing transaction. When using "add_concurrent_index" make sure that this
+  # method is the _only_ method called in the migration, any other changes
+  # should go in a separate migration. This ensures that upon failure _only_ the
+  # index creation fails and can be retried or reverted easily.
+  #
+  # To disable transactions uncomment the following line and remove these
+  # comments:
+  # disable_ddl_transaction!
+
+  def change
+    add_column :members, :expires_at, :datetime
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 2d2ae5fd840..d1ebbe082d1 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20160722221922) do
+ActiveRecord::Schema.define(version: 20160801163421) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -581,6 +581,7 @@ ActiveRecord::Schema.define(version: 20160722221922) do
     t.string   "invite_token"
     t.datetime "invite_accepted_at"
     t.datetime "requested_at"
+    t.datetime "expires_at"
   end
 
   add_index "members", ["access_level"], name: "index_members_on_access_level", using: :btree
-- 
GitLab