Commit 76f2e065ea51f6364b1ca3dc0b1d29d6f52a4b04

Authored by Sytse Sijbrandij
2 parents 8ccad7c3 48d37272

Merge branch 'master' into install-guide-improvements

Conflicts:
	doc/install/installation.md
Showing 64 changed files with 661 additions and 384 deletions   Show diff stats
@@ -24,6 +24,7 @@ v 5.3.0 @@ -24,6 +24,7 @@ v 5.3.0
24 - init.d: Ensure socket is removed before starting service 24 - init.d: Ensure socket is removed before starting service
25 - Admin area: Style teams:index, group:show pages 25 - Admin area: Style teams:index, group:show pages
26 - Own page for failed forking 26 - Own page for failed forking
  27 + - Scrum view for milestone
27 28
28 v 5.2.0 29 v 5.2.0
29 - Turbolinks 30 - Turbolinks
@@ -29,7 +29,7 @@ gem 'gitlab_git', '~> 1.3.0' @@ -29,7 +29,7 @@ gem 'gitlab_git', '~> 1.3.0'
29 gem 'gitlab-grack', '~> 1.0.1', require: 'grack' 29 gem 'gitlab-grack', '~> 1.0.1', require: 'grack'
30 30
31 # LDAP Auth 31 # LDAP Auth
32 -gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap" 32 +gem 'gitlab_omniauth-ldap', '1.0.3', require: "omniauth-ldap"
33 33
34 # Syntax highlighter 34 # Syntax highlighter
35 gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb' 35 gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb'
@@ -72,6 +72,9 @@ gem "seed-fu" @@ -72,6 +72,9 @@ gem "seed-fu"
72 gem "redcarpet", "~> 2.2.2" 72 gem "redcarpet", "~> 2.2.2"
73 gem "github-markup", "~> 0.7.4", require: 'github/markup' 73 gem "github-markup", "~> 0.7.4", require: 'github/markup'
74 74
  75 +# Asciidoc to HTML
  76 +gem "asciidoctor"
  77 +
75 # Servers 78 # Servers
76 gem "puma", '~> 2.0.1' 79 gem "puma", '~> 2.0.1'
77 80
@@ -46,6 +46,7 @@ GEM @@ -46,6 +46,7 @@ GEM
46 rails (~> 3.0) 46 rails (~> 3.0)
47 addressable (2.3.4) 47 addressable (2.3.4)
48 arel (3.0.2) 48 arel (3.0.2)
  49 + asciidoctor (0.1.3)
49 awesome_print (1.1.0) 50 awesome_print (1.1.0)
50 backports (2.6.7) 51 backports (2.6.7)
51 bcrypt-ruby (3.0.1) 52 bcrypt-ruby (3.0.1)
@@ -168,8 +169,8 @@ GEM @@ -168,8 +169,8 @@ GEM
168 github-linguist (~> 2.3.4) 169 github-linguist (~> 2.3.4)
169 gitlab-grit (~> 2.5.1) 170 gitlab-grit (~> 2.5.1)
170 gitlab_meta (5.0) 171 gitlab_meta (5.0)
171 - gitlab_omniauth-ldap (1.0.2)  
172 - net-ldap (~> 0.2.2) 172 + gitlab_omniauth-ldap (1.0.3)
  173 + net-ldap (~> 0.3.1)
173 omniauth (~> 1.0) 174 omniauth (~> 1.0)
174 pyu-ruby-sasl (~> 0.0.3.1) 175 pyu-ruby-sasl (~> 0.0.3.1)
175 rubyntlm (~> 0.1.1) 176 rubyntlm (~> 0.1.1)
@@ -265,7 +266,7 @@ GEM @@ -265,7 +266,7 @@ GEM
265 multi_xml (0.5.3) 266 multi_xml (0.5.3)
266 multipart-post (1.2.0) 267 multipart-post (1.2.0)
267 mysql2 (0.3.11) 268 mysql2 (0.3.11)
268 - net-ldap (0.2.2) 269 + net-ldap (0.3.1)
269 nokogiri (1.5.9) 270 nokogiri (1.5.9)
270 oauth (0.4.7) 271 oauth (0.4.7)
271 oauth2 (0.8.1) 272 oauth2 (0.8.1)
@@ -517,6 +518,7 @@ PLATFORMS @@ -517,6 +518,7 @@ PLATFORMS
517 DEPENDENCIES 518 DEPENDENCIES
518 acts-as-taggable-on 519 acts-as-taggable-on
519 annotate! 520 annotate!
  521 + asciidoctor
520 awesome_print 522 awesome_print
521 better_errors 523 better_errors
522 binding_of_caller 524 binding_of_caller
@@ -544,7 +546,7 @@ DEPENDENCIES @@ -544,7 +546,7 @@ DEPENDENCIES
544 gitlab-pygments.rb (~> 0.3.2) 546 gitlab-pygments.rb (~> 0.3.2)
545 gitlab_git (~> 1.3.0) 547 gitlab_git (~> 1.3.0)
546 gitlab_meta (= 5.0) 548 gitlab_meta (= 5.0)
547 - gitlab_omniauth-ldap (= 1.0.2) 549 + gitlab_omniauth-ldap (= 1.0.3)
548 gon 550 gon
549 grape (~> 0.4.1) 551 grape (~> 0.4.1)
550 grape-entity (~> 0.3.0) 552 grape-entity (~> 0.3.0)
1 -5.3.0.beta1 1 +5.3.0
app/assets/javascripts/milestones.js.coffee
@@ -1,14 +0,0 @@ @@ -1,14 +0,0 @@
1 -$ ->  
2 - $('.milestone-issue-filter li[data-closed]').addClass('hide')  
3 -  
4 - $('.milestone-issue-filter ul.nav li a').click ->  
5 - $('.milestone-issue-filter li').toggleClass('active')  
6 - $('.milestone-issue-filter li[data-closed]').toggleClass('hide')  
7 - false  
8 -  
9 - $('.milestone-merge-requests-filter li[data-closed]').addClass('hide')  
10 -  
11 - $('.milestone-merge-requests-filter ul.nav li a').click ->  
12 - $('.milestone-merge-requests-filter li').toggleClass('active')  
13 - $('.milestone-merge-requests-filter li[data-closed]').toggleClass('hide')  
14 - false  
app/assets/stylesheets/gitlab_bootstrap/nav.scss
@@ -57,6 +57,7 @@ @@ -57,6 +57,7 @@
57 border-color: #CCC; 57 border-color: #CCC;
58 border-bottom: 1px solid #fff; 58 border-bottom: 1px solid #fff;
59 color: #333; 59 color: #333;
  60 + font-weight: bold;
60 } 61 }
61 } 62 }
62 } 63 }
app/assets/stylesheets/sections/events.scss
@@ -58,6 +58,7 @@ @@ -58,6 +58,7 @@
58 background: #f9f9f9; 58 background: #f9f9f9;
59 border-radius: 0; 59 border-radius: 0;
60 color: #555; 60 color: #555;
  61 + margin: 0 20px;
61 } 62 }
62 63
63 .note-file-attach { 64 .note-file-attach {
app/assets/stylesheets/sections/notes.scss
@@ -92,6 +92,10 @@ ul.notes { @@ -92,6 +92,10 @@ ul.notes {
92 .note-body { 92 .note-body {
93 @include md-typography; 93 @include md-typography;
94 margin-left: 45px; 94 margin-left: 45px;
  95 +
  96 + .highlight {
  97 + @include border-radius(4px);
  98 + }
95 } 99 }
96 .note-header { 100 .note-header {
97 padding-bottom: 5px; 101 padding-bottom: 5px;
app/contexts/issues/list_context.rb
@@ -8,7 +8,7 @@ module Issues @@ -8,7 +8,7 @@ module Issues
8 @issues = case params[:status] 8 @issues = case params[:status]
9 when issues_filter[:all] then @project.issues 9 when issues_filter[:all] then @project.issues
10 when issues_filter[:closed] then @project.issues.closed 10 when issues_filter[:closed] then @project.issues.closed
11 - when issues_filter[:to_me] then @project.issues.assigned(current_user) 11 + when issues_filter[:to_me] then @project.issues.assigned_to(current_user)
12 when issues_filter[:by_me] then @project.issues.authored(current_user) 12 when issues_filter[:by_me] then @project.issues.authored(current_user)
13 else @project.issues.opened 13 else @project.issues.opened
14 end 14 end
app/contexts/projects/create_context.rb
@@ -51,6 +51,7 @@ module Projects @@ -51,6 +51,7 @@ module Projects
51 if shell.import_repository(@project.path_with_namespace, @project.import_url) 51 if shell.import_repository(@project.path_with_namespace, @project.import_url)
52 # We should create satellite for imported repo 52 # We should create satellite for imported repo
53 @project.satellite.create unless @project.satellite.exists? 53 @project.satellite.create unless @project.satellite.exists?
  54 + @project.imported = true
54 true 55 true
55 else 56 else
56 @project.errors.add(:import_url, 'cannot clone repo') 57 @project.errors.add(:import_url, 'cannot clone repo')
app/controllers/admin/groups_controller.rb
@@ -8,10 +8,6 @@ class Admin::GroupsController < Admin::ApplicationController @@ -8,10 +8,6 @@ class Admin::GroupsController < Admin::ApplicationController
8 end 8 end
9 9
10 def show 10 def show
11 - @projects = Project.scoped  
12 - @projects = @projects.not_in_group(@group) if @group.projects.present?  
13 - @projects = @projects.all  
14 - @projects.reject!(&:empty_repo?)  
15 end 11 end
16 12
17 def new 13 def new
app/controllers/admin/users_controller.rb
@@ -55,8 +55,14 @@ class Admin::UsersController < Admin::ApplicationController @@ -55,8 +55,14 @@ class Admin::UsersController < Admin::ApplicationController
55 def create 55 def create
56 admin = params[:user].delete("admin") 56 admin = params[:user].delete("admin")
57 57
58 - @admin_user = User.new(params[:user], as: :admin) 58 + opts = {
  59 + force_random_password: true,
  60 + password_expires_at: Time.now
  61 + }
  62 +
  63 + @admin_user = User.new(params[:user].merge(opts), as: :admin)
59 @admin_user.admin = (admin && admin.to_i > 0) 64 @admin_user.admin = (admin && admin.to_i > 0)
  65 + @admin_user.created_by_id = current_user.id
60 66
61 respond_to do |format| 67 respond_to do |format|
62 if @admin_user.save 68 if @admin_user.save
app/controllers/application_controller.rb
1 class ApplicationController < ActionController::Base 1 class ApplicationController < ActionController::Base
2 before_filter :authenticate_user! 2 before_filter :authenticate_user!
3 before_filter :reject_blocked! 3 before_filter :reject_blocked!
  4 + before_filter :check_password_expiration
4 before_filter :set_current_user_for_thread 5 before_filter :set_current_user_for_thread
5 before_filter :add_abilities 6 before_filter :add_abilities
6 before_filter :dev_tools if Rails.env == 'development' 7 before_filter :dev_tools if Rails.env == 'development'
@@ -156,4 +157,10 @@ class ApplicationController &lt; ActionController::Base @@ -156,4 +157,10 @@ class ApplicationController &lt; ActionController::Base
156 gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url 157 gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
157 gon.relative_url_root = Gitlab.config.gitlab.relative_url_root 158 gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
158 end 159 end
  160 +
  161 + def check_password_expiration
  162 + if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now
  163 + redirect_to new_profile_password_path and return
  164 + end
  165 + end
159 end 166 end
app/controllers/passwords_controller.rb 0 → 100644
@@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
  1 +class PasswordsController < ApplicationController
  2 + layout 'navless'
  3 +
  4 + skip_before_filter :check_password_expiration
  5 +
  6 + before_filter :set_user
  7 + before_filter :set_title
  8 +
  9 + def new
  10 + end
  11 +
  12 + def create
  13 + new_password = params[:user][:password]
  14 + new_password_confirmation = params[:user][:password_confirmation]
  15 +
  16 + result = @user.update_attributes(
  17 + password: new_password,
  18 + password_confirmation: new_password_confirmation
  19 + )
  20 +
  21 + if result
  22 + @user.update_attributes(password_expires_at: nil)
  23 + redirect_to root_path, notice: 'Password successfully changed'
  24 + else
  25 + render :new
  26 + end
  27 + end
  28 +
  29 + private
  30 +
  31 + def set_user
  32 + @user = current_user
  33 + end
  34 +
  35 + def set_title
  36 + @title = "New password"
  37 + end
  38 +end
app/controllers/snippets_controller.rb
@@ -7,8 +7,12 @@ class SnippetsController &lt; ApplicationController @@ -7,8 +7,12 @@ class SnippetsController &lt; ApplicationController
7 # Allow destroy snippet 7 # Allow destroy snippet
8 before_filter :authorize_admin_snippet!, only: [:destroy] 8 before_filter :authorize_admin_snippet!, only: [:destroy]
9 9
  10 + before_filter :set_title
  11 +
10 respond_to :html 12 respond_to :html
11 13
  14 + layout 'navless'
  15 +
12 def index 16 def index
13 @snippets = Snippet.public.fresh.non_expired.page(params[:page]).per(20) 17 @snippets = Snippet.public.fresh.non_expired.page(params[:page]).per(20)
14 end 18 end
@@ -98,4 +102,8 @@ class SnippetsController &lt; ApplicationController @@ -98,4 +102,8 @@ class SnippetsController &lt; ApplicationController
98 def authorize_admin_snippet! 102 def authorize_admin_snippet!
99 return render_404 unless can?(current_user, :admin_personal_snippet, @snippet) 103 return render_404 unless can?(current_user, :admin_personal_snippet, @snippet)
100 end 104 end
  105 +
  106 + def set_title
  107 + @title = 'Snippets'
  108 + end
101 end 109 end
app/helpers/application_helper.rb
@@ -142,7 +142,7 @@ module ApplicationHelper @@ -142,7 +142,7 @@ module ApplicationHelper
142 end 142 end
143 143
144 def user_color_scheme_class 144 def user_color_scheme_class
145 - COLOR_SCHEMES[current_user.try(:color_scheme_id)] 145 + COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
146 end 146 end
147 147
148 # Define whenever show last push event 148 # Define whenever show last push event
app/helpers/projects_helper.rb
@@ -48,4 +48,36 @@ module ProjectsHelper @@ -48,4 +48,36 @@ module ProjectsHelper
48 def remove_project_message(project) 48 def remove_project_message(project)
49 "You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?" 49 "You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?"
50 end 50 end
  51 +
  52 + def project_nav_tabs
  53 + @nav_tabs ||= get_project_nav_tabs(@project, current_user)
  54 + end
  55 +
  56 + def project_nav_tab?(name)
  57 + project_nav_tabs.include? name
  58 + end
  59 +
  60 + private
  61 +
  62 + def get_project_nav_tabs(project, current_user)
  63 + nav_tabs = [:home]
  64 +
  65 + if project.repo_exists? && can?(current_user, :download_code, project)
  66 + nav_tabs << [:files, :commits, :network, :graphs]
  67 + end
  68 +
  69 + if project.repo_exists? && project.merge_requests_enabled
  70 + nav_tabs << :merge_requests
  71 + end
  72 +
  73 + if can?(current_user, :admin_project, project)
  74 + nav_tabs << :settings
  75 + end
  76 +
  77 + [:issues, :wiki, :wall, :snippets].each do |feature|
  78 + nav_tabs << feature if project.send :"#{feature}_enabled"
  79 + end
  80 +
  81 + nav_tabs.flatten
  82 + end
51 end 83 end
app/models/concerns/issuable.rb
@@ -22,8 +22,10 @@ module Issuable @@ -22,8 +22,10 @@ module Issuable
22 scope :closed, -> { with_state(:closed) } 22 scope :closed, -> { with_state(:closed) }
23 scope :of_group, ->(group) { where(project_id: group.project_ids) } 23 scope :of_group, ->(group) { where(project_id: group.project_ids) }
24 scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) } 24 scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
25 - scope :assigned, ->(u) { where(assignee_id: u.id)} 25 + scope :assigned_to, ->(u) { where(assignee_id: u.id)}
26 scope :recent, -> { order("created_at DESC") } 26 scope :recent, -> { order("created_at DESC") }
  27 + scope :assigned, -> { where("assignee_id IS NOT NULL") }
  28 + scope :unassigned, -> { where("assignee_id IS NULL") }
27 29
28 delegate :name, 30 delegate :name,
29 :email, 31 :email,
app/models/issue.rb
@@ -27,7 +27,7 @@ class Issue &lt; ActiveRecord::Base @@ -27,7 +27,7 @@ class Issue &lt; ActiveRecord::Base
27 27
28 scope :cared, ->(user) { where(assignee_id: user) } 28 scope :cared, ->(user) { where(assignee_id: user) }
29 scope :authored, ->(user) { where(author_id: user) } 29 scope :authored, ->(user) { where(author_id: user) }
30 - scope :open_for, ->(user) { opened.assigned(user) } 30 + scope :open_for, ->(user) { opened.assigned_to(user) }
31 31
32 state_machine :state, initial: :opened do 32 state_machine :state, initial: :opened do
33 event :close do 33 event :close do
app/models/merge_request.rb
@@ -91,6 +91,15 @@ class MergeRequest &lt; ActiveRecord::Base @@ -91,6 +91,15 @@ class MergeRequest &lt; ActiveRecord::Base
91 if target_branch == source_branch 91 if target_branch == source_branch
92 errors.add :branch_conflict, "You can not use same branch for source and target branches" 92 errors.add :branch_conflict, "You can not use same branch for source and target branches"
93 end 93 end
  94 +
  95 + if opened? || reopened?
  96 + similar_mrs = self.project.merge_requests.where(source_branch: source_branch, target_branch: target_branch).opened
  97 + similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id
  98 +
  99 + if similar_mrs.any?
  100 + errors.add :base, "There is already an open merge request for this branches"
  101 + end
  102 + end
94 end 103 end
95 104
96 def reload_code 105 def reload_code
app/models/namespace.rb
@@ -27,6 +27,7 @@ class Namespace &lt; ActiveRecord::Base @@ -27,6 +27,7 @@ class Namespace &lt; ActiveRecord::Base
27 message: "only letters, digits, spaces & '_' '-' '.' allowed." } 27 message: "only letters, digits, spaces & '_' '-' '.' allowed." }
28 validates :description, length: { within: 0..255 } 28 validates :description, length: { within: 0..255 }
29 validates :path, uniqueness: true, presence: true, length: { within: 1..255 }, 29 validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
  30 + exclusion: { in: Gitlab::Blacklist.path },
30 format: { with: Gitlab::Regex.path_regex, 31 format: { with: Gitlab::Regex.path_regex,
31 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } 32 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
32 33
app/models/project.rb
@@ -79,6 +79,7 @@ class Project &lt; ActiveRecord::Base @@ -79,6 +79,7 @@ class Project &lt; ActiveRecord::Base
79 format: { with: Gitlab::Regex.project_name_regex, 79 format: { with: Gitlab::Regex.project_name_regex,
80 message: "only letters, digits, spaces & '_' '-' '.' allowed. Letter should be first" } 80 message: "only letters, digits, spaces & '_' '-' '.' allowed. Letter should be first" }
81 validates :path, presence: true, length: { within: 0..255 }, 81 validates :path, presence: true, length: { within: 0..255 },
  82 + exclusion: { in: Gitlab::Blacklist.path },
82 format: { with: Gitlab::Regex.path_regex, 83 format: { with: Gitlab::Regex.path_regex,
83 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } 84 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
84 validates :issues_enabled, :wall_enabled, :merge_requests_enabled, 85 validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
@@ -92,7 +93,7 @@ class Project &lt; ActiveRecord::Base @@ -92,7 +93,7 @@ class Project &lt; ActiveRecord::Base
92 format: { with: URI::regexp(%w(http https)), message: "should be a valid url" }, 93 format: { with: URI::regexp(%w(http https)), message: "should be a valid url" },
93 if: :import? 94 if: :import?
94 95
95 - validate :check_limit, :repo_name 96 + validate :check_limit
96 97
97 # Scopes 98 # Scopes
98 scope :without_user, ->(user) { where("projects.id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) } 99 scope :without_user, ->(user) { where("projects.id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
@@ -166,14 +167,6 @@ class Project &lt; ActiveRecord::Base @@ -166,14 +167,6 @@ class Project &lt; ActiveRecord::Base
166 errors[:base] << ("Can't check your ability to create project") 167 errors[:base] << ("Can't check your ability to create project")
167 end 168 end
168 169
169 - def repo_name  
170 - denied_paths = %w(admin dashboard groups help profile projects search)  
171 -  
172 - if denied_paths.include?(path)  
173 - errors.add(:path, "like #{path} is not allowed")  
174 - end  
175 - end  
176 -  
177 def to_param 170 def to_param
178 if namespace 171 if namespace
179 namespace.path + "/" + path 172 namespace.path + "/" + path
@@ -420,6 +413,10 @@ class Project &lt; ActiveRecord::Base @@ -420,6 +413,10 @@ class Project &lt; ActiveRecord::Base
420 !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?) 413 !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
421 end 414 end
422 415
  416 + def imported?
  417 + imported
  418 + end
  419 +
423 def rename_repo 420 def rename_repo
424 old_path_with_namespace = File.join(namespace_dir, path_was) 421 old_path_with_namespace = File.join(namespace_dir, path_was)
425 new_path_with_namespace = File.join(namespace_dir, path) 422 new_path_with_namespace = File.join(namespace_dir, path)
app/models/user.rb
@@ -42,8 +42,11 @@ class User &lt; ActiveRecord::Base @@ -42,8 +42,11 @@ class User &lt; ActiveRecord::Base
42 42
43 attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username, 43 attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
44 :skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password, 44 :skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password,
45 - :extern_uid, :provider, as: [:default, :admin]  
46 - attr_accessible :projects_limit, :can_create_team, :can_create_group, as: :admin 45 + :extern_uid, :provider, :password_expires_at,
  46 + as: [:default, :admin]
  47 +
  48 + attr_accessible :projects_limit, :can_create_team, :can_create_group,
  49 + as: :admin
47 50
48 attr_accessor :force_random_password 51 attr_accessor :force_random_password
49 52
@@ -104,6 +107,7 @@ class User &lt; ActiveRecord::Base @@ -104,6 +107,7 @@ class User &lt; ActiveRecord::Base
104 validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider} 107 validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
105 validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0} 108 validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
106 validates :username, presence: true, uniqueness: true, 109 validates :username, presence: true, uniqueness: true,
  110 + exclusion: { in: Gitlab::Blacklist.path },
107 format: { with: Gitlab::Regex.username_regex, 111 format: { with: Gitlab::Regex.username_regex,
108 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } 112 message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
109 113
@@ -363,4 +367,8 @@ class User &lt; ActiveRecord::Base @@ -363,4 +367,8 @@ class User &lt; ActiveRecord::Base
363 def accessible_deploy_keys 367 def accessible_deploy_keys
364 DeployKey.in_projects(self.master_projects).uniq 368 DeployKey.in_projects(self.master_projects).uniq
365 end 369 end
  370 +
  371 + def created_by
  372 + User.find_by_id(created_by_id) if created_by_id
  373 + end
366 end 374 end
app/observers/project_observer.rb
1 class ProjectObserver < BaseObserver 1 class ProjectObserver < BaseObserver
2 def after_create(project) 2 def after_create(project)
3 - unless project.forked?  
4 - GitlabShellWorker.perform_async(  
5 - :add_repository,  
6 - project.path_with_namespace  
7 - )  
8 -  
9 - log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")  
10 - end 3 + return true if project.forked? || project.imported?
  4 +
  5 + GitlabShellWorker.perform_async(
  6 + :add_repository,
  7 + project.path_with_namespace
  8 + )
  9 +
  10 + log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
11 end 11 end
12 12
13 def after_update(project) 13 def after_update(project)
app/views/admin/teams/members/new.html.haml
1 %h3.page_title 1 %h3.page_title
2 - Team: #{@team.name}  
3 -  
4 -%fieldset  
5 - %legend Members (#{@team.members.count})  
6 - = form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do  
7 - %table#members_list  
8 - %thead  
9 - %tr  
10 - %th User name  
11 - %th Default project access  
12 - %th Team access  
13 - %th  
14 - - @team.members.each do |member|  
15 - %tr.member  
16 - %td  
17 - = link_to [:admin, member] do  
18 - = member.name  
19 - %small= "(#{member.email})"  
20 - %td= @team.human_default_projects_access(member)  
21 - %td= @team.admin?(member) ? "Admin" : "Member"  
22 - %td  
23 - %tr  
24 - %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_username), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'  
25 - %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }  
26 - %td  
27 - %span= check_box_tag :group_admin  
28 - %span Admin?  
29 - %td= submit_tag 'Add', class: "btn btn-primary", id: :add_members_to_team 2 + New members for
  3 + = link_to @team.name, admin_team_path(@team)
  4 + team
  5 +%hr
  6 += form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do
  7 + - if @team.errors.any?
  8 + .alert.alert-error
  9 + %span= @team.errors.full_messages.first
  10 + .clearfix
  11 + = label_tag :user_ids do
  12 + Users to add
  13 + .input
  14 + = select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_username), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
  15 + .clearfix.group-description-holder
  16 + = label_tag :default_project_access do
  17 + Default permission in projects
  18 + .input
  19 + = select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
  20 + .clearfix
  21 + = label_tag :group_admin do
  22 + Is team admin
  23 + .input
  24 + = check_box_tag :group_admin
  25 + .clearfix.form-actions
  26 + = submit_tag 'Add users into team', class: "btn btn-primary", id: :add_members_to_team
  27 + = link_to 'Cancel', :back, class: "btn"
app/views/admin/users/_form.html.haml
@@ -24,19 +24,25 @@ @@ -24,19 +24,25 @@
24 = f.text_field :email, required: true, autocomplete: "off" 24 = f.text_field :email, required: true, autocomplete: "off"
25 %span.help-inline * required 25 %span.help-inline * required
26 26
27 - %fieldset  
28 - %legend Password  
29 - .clearfix  
30 - = f.label :password  
31 - .input= f.password_field :password, disabled: f.object.force_random_password  
32 - .clearfix  
33 - = f.label :password_confirmation  
34 - .input= f.password_field :password_confirmation, disabled: f.object.force_random_password  
35 - -if f.object.new_record? 27 + - if @admin_user.new_record?
  28 + %fieldset
  29 + %legend Password
  30 + .clearfix
  31 + = f.label :password
  32 + .input
  33 + %strong
  34 + A temporary password will be generated and sent to user.
  35 + %br
  36 + User will be forced to change it after first sign in
  37 + - else
  38 + %fieldset
  39 + %legend Password
  40 + .clearfix
  41 + = f.label :password
  42 + .input= f.password_field :password, disabled: f.object.force_random_password
36 .clearfix 43 .clearfix
37 - = f.label :force_random_password do  
38 - %span Generate random password  
39 - .input= f.check_box :force_random_password, {}, true, nil 44 + = f.label :password_confirmation
  45 + .input= f.password_field :password_confirmation, disabled: f.object.force_random_password
40 46
41 %fieldset 47 %fieldset
42 %legend Access 48 %legend Access
app/views/admin/users/show.html.haml
  1 +%h3.page_title
  2 + User:
  3 + = @admin_user.name
  4 + - if @admin_user.blocked?
  5 + %span.cred (Blocked)
  6 + - if @admin_user.admin
  7 + %span.cred (Admin)
  8 +
  9 + .pull-right
  10 + = link_to edit_admin_user_path(@admin_user), class: "btn grouped btn-small" do
  11 + %i.icon-edit
  12 + Edit
  13 + - unless @admin_user == current_user
  14 + - if @admin_user.blocked?
  15 + = link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn grouped btn-small success"
  16 + - else
  17 + = link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn grouped btn-small btn-remove"
  18 + = link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn grouped btn-small btn-remove"
  19 +%hr
  20 +
1 .row 21 .row
2 .span6 22 .span6
3 - %h3.page_title  
4 - = image_tag gravatar_icon(@admin_user.email, 90), class: "avatar s90"  
5 - = @admin_user.name  
6 - - if @admin_user.blocked?  
7 - %span.cred (Blocked)  
8 - - if @admin_user.admin  
9 - %span.cred (Admin)  
10 - .pull-right  
11 - = link_to edit_admin_user_path(@admin_user), class: "btn pull-right" do  
12 - %i.icon-edit  
13 - Edit  
14 - %br  
15 - %small @#{@admin_user.username}  
16 - %br  
17 - %small member since #{@admin_user.created_at.stamp("Nov 12, 2031")}  
18 - .clearfix  
19 - %hr  
20 - %p  
21 - %span.btn.btn-small  
22 - %i.icon-envelope  
23 - = mail_to @admin_user.email  
24 - - unless @admin_user == current_user  
25 - - if @admin_user.blocked?  
26 - = link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small success"  
27 - - else  
28 - = link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"  
29 - = link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-small btn-remove" 23 + .ui-box
  24 + %h5.title
  25 + Account:
  26 + .pull-right
  27 + = image_tag gravatar_icon(@admin_user.email, 32), class: "avatar s32"
  28 + %ul.well-list
  29 + %li
  30 + %span.light Name:
  31 + %strong= @admin_user.name
  32 + %li
  33 + %span.light Username:
  34 + %strong
  35 + = @admin_user.username
  36 + %li
  37 + %span.light Email:
  38 + %strong
  39 + = mail_to @admin_user.email
  40 +
  41 + %li
  42 + %span.light Member since:
  43 + %strong
  44 + = @admin_user.created_at.stamp("Nov 12, 2031")
  45 +
  46 + %li
  47 + %span.light Last sign-in at:
  48 + %strong
  49 + - if @admin_user.last_sign_in_at
  50 + = @admin_user.last_sign_in_at.stamp("Nov 12, 2031")
  51 + - else
  52 + never
  53 +
  54 + - if @admin_user.ldap_user?
  55 + %li
  56 + %span.light LDAP uid:
  57 + %strong
  58 + = @admin_user.extern_uid
  59 +
  60 + - if @admin_user.created_by
  61 + %li
  62 + %span.light Created by:
  63 + %strong
  64 + = link_to @admin_user.created_by.name, [:admin, @admin_user.created_by]
  65 +
30 %hr 66 %hr
31 %h5 67 %h5
32 Add User to Projects 68 Add User to Projects
@@ -67,11 +103,11 @@ @@ -67,11 +103,11 @@
67 103
68 104
69 .span6 105 .span6
70 - = render 'users/profile', user: @admin_user  
71 .ui-box 106 .ui-box
72 %h5.title Projects (#{@projects.count}) 107 %h5.title Projects (#{@projects.count})
73 %ul.well-list 108 %ul.well-list
74 - @projects.sort_by(&:name_with_namespace).each do |project| 109 - @projects.sort_by(&:name_with_namespace).each do |project|
  110 + - tm = project.team.get_tm(@admin_user.id)
75 %li 111 %li
76 = link_to admin_project_path(project), class: dom_class(project) do 112 = link_to admin_project_path(project), class: dom_class(project) do
77 - if project.namespace 113 - if project.namespace
@@ -79,16 +115,17 @@ @@ -79,16 +115,17 @@
79 \/ 115 \/
80 %strong.well-title 116 %strong.well-title
81 = truncate(project.name, length: 45) 117 = truncate(project.name, length: 45)
82 - %span.pull-right.light  
83 - - if project.owner == @admin_user  
84 - %i.icon-wrench  
85 - - tm = project.team.get_tm(@admin_user.id)  
86 - - if tm  
87 - = tm.project_access_human  
88 - = link_to edit_admin_project_member_path(project, tm.user), class: "btn btn-small" do 118 +
  119 + - if project.owner == @admin_user
  120 + %span.label.label-info owner
  121 +
  122 + - if tm
  123 + .pull-right
  124 + = link_to edit_admin_project_member_path(project, tm.user), class: "btn grouped btn-small" do
89 %i.icon-edit 125 %i.icon-edit
90 - = link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @admin_user), method: :delete, class: "btn btn-small btn-remove" do 126 + = link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @admin_user), method: :delete, class: "btn grouped btn-small btn-remove" do
91 %i.icon-remove 127 %i.icon-remove
92 - %p.light  
93 - %i.icon-wrench  
94 - &ndash; user is a project owner 128 +
  129 + .pull-right.light
  130 + = tm.project_access_human
  131 + &nbsp;
app/views/devise/sessions/_oauth_providers.html.haml
1 -- if enabled_oauth_providers.present? 1 +- providers = (enabled_oauth_providers - [:ldap])
  2 +- if providers.present?
2 %hr 3 %hr
3 %div{:'data-no-turbolink' => 'data-no-turbolink'} 4 %div{:'data-no-turbolink' => 'data-no-turbolink'}
4 %span Sign in with: &nbsp; 5 %span Sign in with: &nbsp;
5 - - (enabled_oauth_providers - [:ldap]).each do |provider| 6 + - providers.each do |provider|
6 %span 7 %span
7 - if default_providers.include?(provider) 8 - if default_providers.include?(provider)
8 = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider) 9 = link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
app/views/edit_tree/show.html.haml
@@ -23,7 +23,7 @@ @@ -23,7 +23,7 @@
23 = hidden_field_tag 'last_commit', @last_commit 23 = hidden_field_tag 'last_commit', @last_commit
24 = hidden_field_tag 'content', '', id: :file_content 24 = hidden_field_tag 'content', '', id: :file_content
25 .commit-button-annotation 25 .commit-button-annotation
26 - = button_tag "Commit", class: 'btn commit-btn js-commit-button' 26 + = button_tag "Commit changes", class: 'btn commit-btn js-commit-button btn-primary'
27 .message 27 .message
28 to branch 28 to branch
29 %strong= @ref 29 %strong= @ref
app/views/layouts/nav/_project.html.haml
@@ -3,43 +3,48 @@ @@ -3,43 +3,48 @@
3 = link_to project_path(@project), title: "Project" do 3 = link_to project_path(@project), title: "Project" do
4 %i.icon-home 4 %i.icon-home
5 5
6 - - unless @project.empty_repo?  
7 - - if can? current_user, :download_code, @project  
8 - = nav_link(controller: %w(tree blob blame)) do  
9 - = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)  
10 - = nav_link(controller: %w(commit commits compare repositories protected_branches)) do  
11 - = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref)  
12 - = nav_link(controller: %w(network)) do  
13 - = link_to "Network", project_network_path(@project, @ref || @repository.root_ref)  
14 - = nav_link(controller: %w(graphs)) do  
15 - = link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref)  
16 -  
17 - - if @project.issues_enabled 6 + - if project_nav_tab? :files
  7 + = nav_link(controller: %w(tree blob blame)) do
  8 + = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)
  9 +
  10 + - if project_nav_tab? :commits
  11 + = nav_link(controller: %w(commit commits compare repositories protected_branches)) do
  12 + = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref)
  13 +
  14 + - if project_nav_tab? :network
  15 + = nav_link(controller: %w(network)) do
  16 + = link_to "Network", project_network_path(@project, @ref || @repository.root_ref)
  17 +
  18 + - if project_nav_tab? :graphs
  19 + = nav_link(controller: %w(graphs)) do
  20 + = link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref)
  21 +
  22 + - if project_nav_tab? :issues
18 = nav_link(controller: %w(issues milestones labels)) do 23 = nav_link(controller: %w(issues milestones labels)) do
19 = link_to url_for_project_issues do 24 = link_to url_for_project_issues do
20 Issues 25 Issues
21 - if @project.used_default_issues_tracker? 26 - if @project.used_default_issues_tracker?
22 %span.count.issue_counter= @project.issues.opened.count 27 %span.count.issue_counter= @project.issues.opened.count
23 28
24 - - if @project.repo_exists? && @project.merge_requests_enabled 29 + - if project_nav_tab? :merge_requests
25 = nav_link(controller: :merge_requests) do 30 = nav_link(controller: :merge_requests) do
26 = link_to project_merge_requests_path(@project) do 31 = link_to project_merge_requests_path(@project) do
27 Merge Requests 32 Merge Requests
28 %span.count.merge_counter= @project.merge_requests.opened.count 33 %span.count.merge_counter= @project.merge_requests.opened.count
29 34
30 - - if @project.wiki_enabled 35 + - if project_nav_tab? :wiki
31 = nav_link(controller: :wikis) do 36 = nav_link(controller: :wikis) do
32 = link_to 'Wiki', project_wiki_path(@project, :home) 37 = link_to 'Wiki', project_wiki_path(@project, :home)
33 38
34 - - if @project.wall_enabled 39 + - if project_nav_tab? :wall
35 = nav_link(controller: :walls) do 40 = nav_link(controller: :walls) do
36 = link_to 'Wall', project_wall_path(@project) 41 = link_to 'Wall', project_wall_path(@project)
37 42
38 - - if @project.snippets_enabled 43 + - if project_nav_tab? :snippets
39 = nav_link(controller: :snippets) do 44 = nav_link(controller: :snippets) do
40 = link_to 'Snippets', project_snippets_path(@project) 45 = link_to 'Snippets', project_snippets_path(@project)
41 46
42 - - if can? current_user, :admin_project, @project 47 + - if project_nav_tab? :settings
43 = nav_link(html_options: {class: "#{project_tab_class}"}) do 48 = nav_link(html_options: {class: "#{project_tab_class}"}) do
44 = link_to edit_project_path(@project), class: "stat-tab tab " do 49 = link_to edit_project_path(@project), class: "stat-tab tab " do
45 Settings 50 Settings
app/views/layouts/nav/_team.html.haml
@@ -18,7 +18,7 @@ @@ -18,7 +18,7 @@
18 Members 18 Members
19 %span.count= @team.members.count 19 %span.count= @team.members.count
20 20
21 - - if can? current_user, :admin_user_team, @team 21 + - if can? current_user, :manage_user_team, @team
22 = nav_link(path: 'teams#edit') do 22 = nav_link(path: 'teams#edit') do
23 = link_to edit_team_path(@team), class: "stat-tab tab " do 23 = link_to edit_team_path(@team), class: "stat-tab tab " do
24 Settings 24 Settings
app/views/layouts/snippets.html.haml
@@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
1 -!!! 5  
2 -%html{ lang: "en"}  
3 - = render "layouts/head", title: "Snipepts"  
4 - %body{class: "#{app_theme} application", :'data-page' => body_data_page}  
5 - = render "layouts/head_panel", title: "Snippets"  
6 - = render "layouts/flash"  
7 - %nav.main-nav  
8 - .container  
9 - %ul  
10 - = nav_link(path: 'snippets#user_index', html_options: {class: 'home'}) do  
11 - = link_to user_snippets_path(current_user), title: "My Snippets" do  
12 - %i.icon-home  
13 - = nav_link(path: 'snippets#new') do  
14 - = link_to new_snippet_path do  
15 - New snippet  
16 - = nav_link(path: 'snippets#index') do  
17 - = link_to snippets_path do  
18 - Discover snippets  
19 - .container  
20 - .content= yield  
app/views/milestones/_issues.html.haml 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +.ui-box
  2 + %h5.title= title
  3 + %ul.well-list
  4 + - issues.each do |issue|
  5 + %li
  6 + = link_to [@project, issue] do
  7 + %span.badge{class: issue.closed? ? 'badge-important' : 'badge-info'} ##{issue.id}
  8 + = link_to_gfm truncate(issue.title, length: 60), [@project, issue]
  9 + - if issue.assignee
  10 + .pull-right
  11 + = image_tag gravatar_icon(issue.assignee.email, 16), class: "avatar s16"
app/views/milestones/_merge_request.html.haml 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +%li
  2 + = link_to [@project, merge_request] do
  3 + %span.badge.badge-info ##{merge_request.id}
  4 + &ndash;
  5 + = link_to_gfm truncate(merge_request.title, length: 60), [@project, merge_request]
app/views/milestones/show.html.haml
@@ -55,39 +55,52 @@ @@ -55,39 +55,52 @@
55 = markdown @milestone.description 55 = markdown @milestone.description
56 56
57 57
58 -.row  
59 - .span6  
60 - .ui-box.milestone-issue-filter  
61 - .title  
62 - %ul.nav.nav-pills  
63 - %li.active= link_to('Open Issues', '#')  
64 - %li=link_to('All Issues', '#')  
65 - %ul.well-list  
66 - - @issues.each do |issue|  
67 - %li{data: {closed: issue.closed?}}  
68 - = link_to [@project, issue] do  
69 - %span.badge.badge-info ##{issue.id}  
70 - &ndash;  
71 - = link_to_gfm truncate(issue.title, length: 60), [@project, issue] 58 +%ul.nav.nav-tabs
  59 + %li.active
  60 + = link_to '#tab-issues', 'data-toggle' => 'tab' do
  61 + Issues
  62 + %span.badge= @issues.count
  63 + %li
  64 + = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do
  65 + Merge Requests
  66 + %span.badge= @merge_requests.count
  67 + %li
  68 + = link_to '#tab-participants', 'data-toggle' => 'tab' do
  69 + Participants
  70 + %span.badge= @users.count
72 71
73 - .span6  
74 - .ui-box.milestone-merge-requests-filter  
75 - .title  
76 - %ul.nav.nav-pills  
77 - %li.active= link_to('Open Merge Requests', '#')  
78 - %li=link_to('All Merge Requests', '#')  
79 - %ul.well-list  
80 - - @merge_requests.each do |merge_request|  
81 - %li{data: {closed: merge_request.closed? || merge_request.merged?}}  
82 - = link_to [@project, merge_request] do  
83 - %span.badge.badge-info ##{merge_request.id}  
84 - &ndash;  
85 - = link_to_gfm truncate(merge_request.title, length: 60), [@project, merge_request]  
86 72
87 -%hr  
88 -%h6 Participants:  
89 -%div  
90 - - @users.each do |user|  
91 - = link_to_member(@project, user) 73 +.tab-content
  74 + .tab-pane.active#tab-issues
  75 + .row
  76 + .span4
  77 + = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned)
  78 + .span4
  79 + = render('issues', title: 'Ongoing Issues (open and assigned)', issues: @issues.opened.assigned)
  80 + .span4
  81 + = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed)
  82 +
  83 + .tab-pane#tab-merge-requests
  84 + .row
  85 + .span6
  86 + .ui-box
  87 + %h5.title Open
  88 + %ul.well-list
  89 + - @merge_requests.opened.each do |merge_request|
  90 + = render 'merge_request', merge_request: merge_request
  91 + .span6
  92 + .ui-box
  93 + %h5.title Closed
  94 + %ul.well-list
  95 + - @merge_requests.closed.each do |merge_request|
  96 + = render 'merge_request', merge_request: merge_request
92 97
93 -.clearfix 98 + .tab-pane#tab-participants
  99 + %ul.bordered-list
  100 + - @users.each do |user|
  101 + %li
  102 + = link_to user, title: user.name, class: "dark" do
  103 + = image_tag gravatar_icon(user.email, 32), class: "avatar s32"
  104 + %strong= truncate(user.name, lenght: 40)
  105 + %br
  106 + %small.cgray= user.username
app/views/notify/new_user_email.html.haml
@@ -8,13 +8,14 @@ @@ -8,13 +8,14 @@
8 %p 8 %p
9 login.......................................... 9 login..........................................
10 %code= @user['email'] 10 %code= @user['email']
11 -%p  
12 - - unless Gitlab.config.gitlab.signup_enabled 11 +
  12 +- if @user.created_by_id
  13 + %p
13 password.................................. 14 password..................................
14 %code= @password 15 %code= @password
15 16
16 -%p  
17 - Please change your password immediately after login. 17 + %p
  18 + You will be forced to change this password immediately after login.
18 19
19 %p 20 %p
20 = link_to "Click here to login", root_url 21 = link_to "Click here to login", root_url
app/views/notify/new_user_email.text.erb
@@ -3,10 +3,11 @@ Hi &lt;%= @user.name %&gt;! @@ -3,10 +3,11 @@ Hi &lt;%= @user.name %&gt;!
3 The Administrator created an account for you. Now you are a member of company GitLab application. 3 The Administrator created an account for you. Now you are a member of company GitLab application.
4 4
5 login.................. <%= @user.email %> 5 login.................. <%= @user.email %>
6 -<% unless Gitlab.config.gitlab.signup_enabled %> 6 +<% if @user.created_by_id %>
7 password............... <%= @password %> 7 password............... <%= @password %>
  8 +
  9 + You will be forced to change this password immediately after login.
8 <% end %> 10 <% end %>
9 11
10 -Please change your password immediately after login.  
11 12
12 Click here to login: <%= url_for(root_url) %> 13 Click here to login: <%= url_for(root_url) %>
app/views/passwords/new.html.haml 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 += form_for @user, url: profile_password_path, method: :post do |f|
  2 + .light-well.padded
  3 + %p.slead
  4 + Please set new password before proceed.
  5 + %br
  6 + After successful password update you will be redirected to login screen
  7 + -if @user.errors.any?
  8 + .alert.alert-error
  9 + %ul
  10 + - @user.errors.full_messages.each do |msg|
  11 + %li= msg
  12 +
  13 + .clearfix
  14 + = f.label :password
  15 + .input= f.password_field :password, required: true
  16 + .clearfix
  17 + = f.label :password_confirmation
  18 + .input
  19 + = f.password_field :password_confirmation, required: true
  20 + .clearfix
  21 + .input
  22 + = f.submit 'Set new password', class: "btn btn-create"
app/views/projects/_settings_nav.html.haml
@@ -5,8 +5,8 @@ @@ -5,8 +5,8 @@
5 Edit 5 Edit
6 = nav_link(controller: [:team_members, :teams]) do 6 = nav_link(controller: [:team_members, :teams]) do
7 = link_to project_team_index_path(@project), class: "team-tab tab" do 7 = link_to project_team_index_path(@project), class: "team-tab tab" do
8 - %i.icon-user  
9 - Team 8 + %i.icon-group
  9 + Project Members
10 = nav_link(controller: :deploy_keys) do 10 = nav_link(controller: :deploy_keys) do
11 = link_to project_deploy_keys_path(@project) do 11 = link_to project_deploy_keys_path(@project) do
12 %span 12 %span
@@ -14,7 +14,7 @@ @@ -14,7 +14,7 @@
14 = nav_link(controller: :hooks) do 14 = nav_link(controller: :hooks) do
15 = link_to project_hooks_path(@project) do 15 = link_to project_hooks_path(@project) do
16 %span 16 %span
17 - Hooks 17 + Web Hooks
18 = nav_link(controller: :services) do 18 = nav_link(controller: :services) do
19 = link_to project_services_path(@project) do 19 = link_to project_services_path(@project) do
20 %span 20 %span
app/views/snippets/current_user_index.html.haml
1 %h3.page_title 1 %h3.page_title
2 My Snippets 2 My Snippets
3 %small share code pastes with others out of git repository 3 %small share code pastes with others out of git repository
4 - = link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do  
5 - Add new snippet 4 + .pull-right
  5 + = link_to new_snippet_path, class: "btn btn-small add_new grouped btn-primary", title: "New Snippet" do
  6 + Add new snippet
  7 + = link_to snippets_path, class: "btn btn-small grouped" do
  8 + Discover snippets
6 9
7 %hr 10 %hr
8 11
app/views/snippets/index.html.haml
1 %h3.page_title 1 %h3.page_title
2 Public snippets 2 Public snippets
3 %small share code pastes with others out of git repository 3 %small share code pastes with others out of git repository
4 - = link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do  
5 - Add new snippet 4 +
  5 + .pull-right
  6 + = link_to new_snippet_path, class: "btn btn-small add_new grouped btn-primary", title: "New Snippet" do
  7 + Add new snippet
  8 + = link_to user_snippets_path(current_user), class: "btn btn-small grouped" do
  9 + My snippets
6 10
7 %hr 11 %hr
8 .row 12 .row
app/views/team_members/index.html.haml
1 = render "projects/settings_nav" 1 = render "projects/settings_nav"
2 %h3.page_title 2 %h3.page_title
3 - Team Members 3 + Project Members
4 (#{@project.users.count}) 4 (#{@project.users.count})
5 %small 5 %small
6 Read more about project permissions 6 Read more about project permissions
app/views/teams/edit.html.haml
@@ -5,8 +5,9 @@ @@ -5,8 +5,9 @@
5 = link_to 'Projects', '#tab-projects', 'data-toggle' => 'tab' 5 = link_to 'Projects', '#tab-projects', 'data-toggle' => 'tab'
6 %li 6 %li
7 = link_to 'Edit Team', '#tab-edit', 'data-toggle' => 'tab' 7 = link_to 'Edit Team', '#tab-edit', 'data-toggle' => 'tab'
8 - %li  
9 - = link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab' 8 + - if can? current_user, :admin_user_team, @team
  9 + %li
  10 + = link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab'
10 11
11 .span9 12 .span9
12 .tab-content 13 .tab-content
config/environments/production.rb
@@ -52,7 +52,7 @@ Gitlab::Application.configure do @@ -52,7 +52,7 @@ Gitlab::Application.configure do
52 # config.action_mailer.raise_delivery_errors = false 52 # config.action_mailer.raise_delivery_errors = false
53 53
54 # Enable threaded mode 54 # Enable threaded mode
55 - config.threadsafe! 55 + config.threadsafe! unless $rails_rake_task
56 56
57 # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 57 # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
58 # the I18n.default_locale when a translation can not be found) 58 # the I18n.default_locale when a translation can not be found)
config/routes.rb
@@ -123,6 +123,7 @@ Gitlab::Application.routes.draw do @@ -123,6 +123,7 @@ Gitlab::Application.routes.draw do
123 end 123 end
124 124
125 resource :notifications 125 resource :notifications
  126 + resource :password
126 end 127 end
127 128
128 resources :keys 129 resources :keys
db/fixtures/production/001_admin.rb
@@ -3,7 +3,8 @@ admin = User.create( @@ -3,7 +3,8 @@ admin = User.create(
3 name: "Administrator", 3 name: "Administrator",
4 username: 'root', 4 username: 'root',
5 password: "5iveL!fe", 5 password: "5iveL!fe",
6 - password_confirmation: "5iveL!fe" 6 + password_confirmation: "5iveL!fe",
  7 + password_expires_at: Time.now
7 ) 8 )
8 9
9 admin.projects_limit = 10000 10 admin.projects_limit = 10000
db/migrate/20130613165816_add_password_expires_at_to_users.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddPasswordExpiresAtToUsers < ActiveRecord::Migration
  2 + def change
  3 + add_column :users, :password_expires_at, :datetime
  4 + end
  5 +end
db/migrate/20130613173246_add_created_by_id_to_user.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddCreatedByIdToUser < ActiveRecord::Migration
  2 + def change
  3 + add_column :users, :created_by_id, :integer
  4 + end
  5 +end
db/migrate/20130614132337_add_improted_to_project.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddImprotedToProject < ActiveRecord::Migration
  2 + def change
  3 + add_column :projects, :imported, :boolean, default: false, null: false
  4 + end
  5 +end
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 # 11 #
12 # It's strongly recommended to check this file into your version control system. 12 # It's strongly recommended to check this file into your version control system.
13 13
14 -ActiveRecord::Schema.define(:version => 20130522141856) do 14 +ActiveRecord::Schema.define(:version => 20130614132337) do
15 15
16 create_table "deploy_keys_projects", :force => true do |t| 16 create_table "deploy_keys_projects", :force => true do |t|
17 t.integer "deploy_key_id", :null => false 17 t.integer "deploy_key_id", :null => false
@@ -172,6 +172,7 @@ ActiveRecord::Schema.define(:version =&gt; 20130522141856) do @@ -172,6 +172,7 @@ ActiveRecord::Schema.define(:version =&gt; 20130522141856) do
172 t.string "issues_tracker_id" 172 t.string "issues_tracker_id"
173 t.boolean "snippets_enabled", :default => true, :null => false 173 t.boolean "snippets_enabled", :default => true, :null => false
174 t.datetime "last_activity_at" 174 t.datetime "last_activity_at"
  175 + t.boolean "imported", :default => false, :null => false
175 end 176 end
176 177
177 add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" 178 add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
@@ -292,6 +293,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130522141856) do @@ -292,6 +293,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130522141856) do
292 t.string "state" 293 t.string "state"
293 t.integer "color_scheme_id", :default => 1, :null => false 294 t.integer "color_scheme_id", :default => 1, :null => false
294 t.integer "notification_level", :default => 1, :null => false 295 t.integer "notification_level", :default => 1, :null => false
  296 + t.datetime "password_expires_at"
  297 + t.integer "created_by_id"
295 end 298 end
296 299
297 add_index "users", ["admin"], :name => "index_users_on_admin" 300 add_index "users", ["admin"], :name => "index_users_on_admin"
doc/api/README.md
@@ -69,15 +69,15 @@ When listing resources you can pass the following parameters: @@ -69,15 +69,15 @@ When listing resources you can pass the following parameters:
69 69
70 ## Contents 70 ## Contents
71 71
72 -+ [Users](doc/api/users.md)  
73 -+ [Session](doc/api/session.md)  
74 -+ [Projects](doc/api/projects.md)  
75 -+ [Project Snippets](doc/api/project_snippets.md)  
76 -+ [Repositories](doc/api/repositories.md)  
77 -+ [Issues](doc/api/issues.md)  
78 -+ [Milestones](doc/api/milestones.md)  
79 -+ [Notes](doc/api/notes.md)  
80 -+ [Deploy Keys](doc/api/deploy_keys.md)  
81 -+ [System Hooks](doc/api/system_hooks.md)  
82 -+ [Groups](doc/api/groups.md)  
83 -+ [User Teams](doc/api/user_teams.md) 72 ++ [Users](users.md)
  73 ++ [Session](session.md)
  74 ++ [Projects](projects.md)
  75 ++ [Project Snippets](project_snippets.md)
  76 ++ [Repositories](repositories.md)
  77 ++ [Issues](issues.md)
  78 ++ [Milestones](milestones.md)
  79 ++ [Notes](notes.md)
  80 ++ [Deploy Keys](deploy_keys.md)
  81 ++ [System Hooks](system_hooks.md)
  82 ++ [Groups](groups.md)
  83 ++ [User Teams](user_teams.md)
doc/install/installation.md
@@ -148,10 +148,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install @@ -148,10 +148,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install
148 cd /home/git/gitlab 148 cd /home/git/gitlab
149 149
150 # Checkout to stable release 150 # Checkout to stable release
151 - sudo -u git -H git checkout 5-2-stable 151 + sudo -u git -H git checkout 5-3-stable
152 152
153 **Note:** 153 **Note:**
154 -You can change `5-2-stable` to `master` if you want the *bleeding edge* version, but do so with caution! 154 +You can change `5-3-stable` to `master` if you want the *bleeding edge* version, but do so with caution!
155 155
156 ## Configure it 156 ## Configure it
157 157
@@ -352,10 +352,10 @@ GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already @@ -352,10 +352,10 @@ GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already
352 352
353 These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation. 353 These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation.
354 354
355 -* Add `gem "omniauth-your-auth-provider"` to the [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/Gemfile#L18) 355 +* Add `gem "omniauth-your-auth-provider"` to the [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/5-3-stable/Gemfile#L18)
356 * Run `sudo -u git -H bundle install` to install the new gem(s) 356 * Run `sudo -u git -H bundle install` to install the new gem(s)
357 -* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/config/gitlab.yml.example#L53) as a reference)  
358 -* Add icons for the new provider into the [vendor/assets/images/authbuttons](https://github.com/gitlabhq/gitlabhq/tree/5-2-stable/vendor/assets/images/authbuttons) directory (you can find some more popular ones over at https://github.com/intridea/authbuttons) 357 +* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/5-3-stable/config/gitlab.yml.example#L53) as a reference)
  358 +* Add icons for the new provider into the [vendor/assets/images/authbuttons](https://github.com/gitlabhq/gitlabhq/tree/5-3-stable/vendor/assets/images/authbuttons) directory (you can find some more popular ones over at https://github.com/intridea/authbuttons)
359 * Restart GitLab 359 * Restart GitLab
360 360
361 ### Examples 361 ### Examples
doc/update/5.2-to-5.3.md 0 → 100644
@@ -0,0 +1,80 @@ @@ -0,0 +1,80 @@
  1 +# From 5.2 to 5.3
  2 +
  3 +### 0. Backup
  4 +
  5 +It's useful to make a backup just in case things go south:
  6 +(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
  7 +
  8 +```bash
  9 +cd /home/git/gitlab
  10 +sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:create
  11 +```
  12 +
  13 +### 1. Stop server
  14 +
  15 + sudo service gitlab stop
  16 +
  17 +### 2. Get latest code
  18 +
  19 +```bash
  20 +cd /home/git/gitlab
  21 +sudo -u git -H git fetch
  22 +sudo -u git -H git checkout 5-3-stable
  23 +```
  24 +
  25 +### 3. Install libs, migrations, etc.
  26 +
  27 +```bash
  28 +cd /home/git/gitlab
  29 +
  30 +# MySQL
  31 +sudo -u git -H bundle install --without development test postgres --deployment
  32 +
  33 +#PostgreSQL
  34 +sudo -u git -H bundle install --without development test mysql --deployment
  35 +
  36 +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
  37 +```
  38 +
  39 +### 4. Update config files
  40 +
  41 +* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/config/gitlab.yml.example but with your settings.
  42 +* Make `/home/git/gitlab/config/puma.rb` same as https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/config/puma.rb.example but with your settings.
  43 +
  44 +### 5. Update Init script
  45 +
  46 +```bash
  47 +sudo rm /etc/init.d/gitlab
  48 +sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/5-3-stable/lib/support/init.d/gitlab
  49 +sudo chmod +x /etc/init.d/gitlab
  50 +```
  51 +
  52 +### 6. Start application
  53 +
  54 + sudo service gitlab start
  55 + sudo service nginx restart
  56 +
  57 +### 7. Check application status
  58 +
  59 +Check if GitLab and its environment are configured correctly:
  60 +
  61 + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
  62 +
  63 +To make sure you didn't miss anything run a more thorough check with:
  64 +
  65 + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
  66 +
  67 +If all items are green, then congratulations upgrade complete!
  68 +
  69 +## Things went south? Revert to previous version (5.2)
  70 +
  71 +### 1. Revert the code to the previous version
  72 +Follow the [`upgrade guide from 5.1 to 5.2`](5.1-to-5.2.md), except for the database migration
  73 +(The backup is already migrated to the previous version)
  74 +
  75 +### 2. Restore from the backup:
  76 +
  77 +```bash
  78 +cd /home/git/gitlab
  79 +sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:restore
  80 +```
features/project/issues/milestones.feature
@@ -22,5 +22,3 @@ Feature: Project Milestones @@ -22,5 +22,3 @@ Feature: Project Milestones
22 Given the milestone has open and closed issues 22 Given the milestone has open and closed issues
23 And I click link "v2.2" 23 And I click link "v2.2"
24 Then I should see 3 issues 24 Then I should see 3 issues
25 - When I click link "All Issues"  
26 - Then I should see 4 issues  
features/steps/project/project_active_tab.rb
@@ -45,7 +45,7 @@ class ProjectActiveTab &lt; Spinach::FeatureSteps @@ -45,7 +45,7 @@ class ProjectActiveTab &lt; Spinach::FeatureSteps
45 # Sub Tabs: Home 45 # Sub Tabs: Home
46 46
47 Given 'I click the "Team" tab' do 47 Given 'I click the "Team" tab' do
48 - click_link('Team') 48 + click_link('Project Members')
49 end 49 end
50 50
51 Given 'I click the "Attachments" tab' do 51 Given 'I click the "Attachments" tab' do
@@ -61,7 +61,7 @@ class ProjectActiveTab &lt; Spinach::FeatureSteps @@ -61,7 +61,7 @@ class ProjectActiveTab &lt; Spinach::FeatureSteps
61 end 61 end
62 62
63 Given 'I click the "Hooks" tab' do 63 Given 'I click the "Hooks" tab' do
64 - click_link('Hooks') 64 + click_link('Web Hooks')
65 end 65 end
66 66
67 Given 'I click the "Deploy Keys" tab' do 67 Given 'I click the "Deploy Keys" tab' do
@@ -73,7 +73,7 @@ class ProjectActiveTab &lt; Spinach::FeatureSteps @@ -73,7 +73,7 @@ class ProjectActiveTab &lt; Spinach::FeatureSteps
73 end 73 end
74 74
75 Then 'the active sub tab should be Team' do 75 Then 'the active sub tab should be Team' do
76 - ensure_active_sub_tab('Team') 76 + ensure_active_sub_tab('Project Members')
77 end 77 end
78 78
79 Then 'the active sub tab should be Attachments' do 79 Then 'the active sub tab should be Attachments' do
@@ -89,7 +89,7 @@ class ProjectActiveTab &lt; Spinach::FeatureSteps @@ -89,7 +89,7 @@ class ProjectActiveTab &lt; Spinach::FeatureSteps
89 end 89 end
90 90
91 Then 'the active sub tab should be Hooks' do 91 Then 'the active sub tab should be Hooks' do
92 - ensure_active_sub_tab('Hooks') 92 + ensure_active_sub_tab('Web Hooks')
93 end 93 end
94 94
95 Then 'the active sub tab should be Deploy Keys' do 95 Then 'the active sub tab should be Deploy Keys' do
features/steps/project/project_merge_requests.rb
@@ -57,8 +57,8 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps @@ -57,8 +57,8 @@ class ProjectMergeRequests &lt; Spinach::FeatureSteps
57 57
58 And 'I submit new merge request "Wiki Feature"' do 58 And 'I submit new merge request "Wiki Feature"' do
59 fill_in "merge_request_title", with: "Wiki Feature" 59 fill_in "merge_request_title", with: "Wiki Feature"
60 - select "master", from: "merge_request_source_branch"  
61 - select "stable", from: "merge_request_target_branch" 60 + select "bootstrap", from: "merge_request_source_branch"
  61 + select "master", from: "merge_request_target_branch"
62 click_button "Submit merge request" 62 click_button "Submit merge request"
63 end 63 end
64 64
features/steps/project/project_milestones.rb
@@ -50,12 +50,6 @@ class ProjectMilestones &lt; Spinach::FeatureSteps @@ -50,12 +50,6 @@ class ProjectMilestones &lt; Spinach::FeatureSteps
50 end 50 end
51 51
52 Then "I should see 3 issues" do 52 Then "I should see 3 issues" do
53 - page.should have_selector('.milestone-issue-filter .well-list li', count: 4)  
54 - page.should have_selector('.milestone-issue-filter .well-list li.hide', count: 1)  
55 - end  
56 -  
57 - Then "I should see 4 issues" do  
58 - page.should have_selector('.milestone-issue-filter .well-list li', count: 4)  
59 - page.should_not have_selector('.milestone-issue-filter .well-list li.hide') 53 + page.should have_selector('#tab-issues li', count: 4)
60 end 54 end
61 end 55 end
features/steps/userteams/userteams.rb
@@ -93,7 +93,7 @@ class Userteams &lt; Spinach::FeatureSteps @@ -93,7 +93,7 @@ class Userteams &lt; Spinach::FeatureSteps
93 Then 'I should see issues from this team assigned to me' do 93 Then 'I should see issues from this team assigned to me' do
94 team = UserTeam.last 94 team = UserTeam.last
95 team.projects.each do |project| 95 team.projects.each do |project|
96 - project.issues.assigned(current_user).each do |issue| 96 + project.issues.assigned_to(current_user).each do |issue|
97 page.should have_content issue.title 97 page.should have_content issue.title
98 end 98 end
99 end 99 end
@@ -121,7 +121,7 @@ class Userteams &lt; Spinach::FeatureSteps @@ -121,7 +121,7 @@ class Userteams &lt; Spinach::FeatureSteps
121 team = UserTeam.last 121 team = UserTeam.last
122 team.projects.each do |project| 122 team.projects.each do |project|
123 team.members.each do |member| 123 team.members.each do |member|
124 - project.issues.assigned(member).each do |issue| 124 + project.issues.assigned_to(member).each do |issue|
125 page.should have_content issue.title 125 page.should have_content issue.title
126 end 126 end
127 end 127 end
@@ -131,9 +131,7 @@ class Userteams &lt; Spinach::FeatureSteps @@ -131,9 +131,7 @@ class Userteams &lt; Spinach::FeatureSteps
131 Given 'project from team has merge requests assigned to me' do 131 Given 'project from team has merge requests assigned to me' do
132 team = UserTeam.last 132 team = UserTeam.last
133 team.projects.each do |project| 133 team.projects.each do |project|
134 - team.members.each do |member|  
135 - 3.times { create(:merge_request, assignee: member, project: project) }  
136 - end 134 + create(:merge_request, assignee: current_user, project: project)
137 end 135 end
138 end 136 end
139 137
@@ -145,10 +143,8 @@ class Userteams &lt; Spinach::FeatureSteps @@ -145,10 +143,8 @@ class Userteams &lt; Spinach::FeatureSteps
145 Then 'I should see merge requests from this team assigned to me' do 143 Then 'I should see merge requests from this team assigned to me' do
146 team = UserTeam.last 144 team = UserTeam.last
147 team.projects.each do |project| 145 team.projects.each do |project|
148 - team.members.each do |member|  
149 - project.issues.assigned(member).each do |merge_request|  
150 - page.should have_content merge_request.title  
151 - end 146 + project.merge_requests.each do |merge_request|
  147 + page.should have_content merge_request.title
152 end 148 end
153 end 149 end
154 end 150 end
@@ -156,20 +152,8 @@ class Userteams &lt; Spinach::FeatureSteps @@ -156,20 +152,8 @@ class Userteams &lt; Spinach::FeatureSteps
156 Given 'project from team has merge requests assigned to team members' do 152 Given 'project from team has merge requests assigned to team members' do
157 team = UserTeam.last 153 team = UserTeam.last
158 team.projects.each do |project| 154 team.projects.each do |project|
159 - team.members.each do |member|  
160 - 3.times { create(:merge_request, assignee: member, project: project) }  
161 - end  
162 - end  
163 - end  
164 -  
165 - Then 'I should see merge requests from this team assigned to me' do  
166 - team = UserTeam.last  
167 - team.projects.each do |project|  
168 - team.members.each do |member|  
169 - project.issues.assigned(member).each do |merge_request|  
170 - page.should have_content merge_request.title  
171 - end  
172 - end 155 + member = team.members.sample
  156 + create(:merge_request, assignee: member, project: project)
173 end 157 end
174 end 158 end
175 159
lib/gitlab/backend/grack_auth.rb
1 require_relative 'shell_env' 1 require_relative 'shell_env'
2 -require 'omniauth-ldap' 2 +require_relative 'grack_ldap'
  3 +require_relative 'grack_helpers'
3 4
4 module Grack 5 module Grack
5 class Auth < Rack::Auth::Basic 6 class Auth < Rack::Auth::Basic
6 - attr_accessor :user, :project 7 + include LDAP
  8 + include Helpers
  9 +
  10 + attr_accessor :user, :project, :ref, :env
7 11
8 def call(env) 12 def call(env)
9 @env = env 13 @env = env
@@ -14,42 +18,52 @@ module Grack @@ -14,42 +18,52 @@ module Grack
14 @env['PATH_INFO'] = @request.path 18 @env['PATH_INFO'] = @request.path
15 @env['SCRIPT_NAME'] = "" 19 @env['SCRIPT_NAME'] = ""
16 20
17 - return render_not_found unless project  
18 - return unauthorized unless project.public || @auth.provided?  
19 - return bad_request if @auth.provided? && !@auth.basic?  
20 -  
21 - if valid?  
22 - if @auth.provided?  
23 - @env['REMOTE_USER'] = @auth.username  
24 - end  
25 - return @app.call(env)  
26 - else  
27 - unauthorized  
28 - end 21 + auth!
29 end 22 end
30 23
31 - def valid? 24 + private
  25 +
  26 + def auth!
  27 + return render_not_found unless project
  28 +
32 if @auth.provided? 29 if @auth.provided?
  30 + return bad_request unless @auth.basic?
  31 +
33 # Authentication with username and password 32 # Authentication with username and password
34 login, password = @auth.credentials 33 login, password = @auth.credentials
35 34
36 - @user = authenticate(login, password)  
37 - return false unless @user 35 + @user = authenticate_user(login, password)
38 36
39 - Gitlab::ShellEnv.set_env(@user) 37 + if @user
  38 + Gitlab::ShellEnv.set_env(@user)
  39 + @env['REMOTE_USER'] = @auth.username
  40 + else
  41 + return unauthorized
  42 + end
  43 +
  44 + else
  45 + return unauthorized unless project.public
40 end 46 end
41 47
  48 + if authorized_git_request?
  49 + @app.call(env)
  50 + else
  51 + unauthorized
  52 + end
  53 + end
  54 +
  55 + def authorized_git_request?
42 # Git upload and receive 56 # Git upload and receive
43 if @request.get? 57 if @request.get?
44 - validate_get_request 58 + authorize_request(@request.params['service'])
45 elsif @request.post? 59 elsif @request.post?
46 - validate_post_request 60 + authorize_request(File.basename(@request.path))
47 else 61 else
48 false 62 false
49 end 63 end
50 end 64 end
51 65
52 - def authenticate(login, password) 66 + def authenticate_user(login, password)
53 user = User.find_by_email(login) || User.find_by_username(login) 67 user = User.find_by_email(login) || User.find_by_username(login)
54 68
55 # If the provided login was not a known email or username 69 # If the provided login was not a known email or username
@@ -65,34 +79,12 @@ module Grack @@ -65,34 +79,12 @@ module Grack
65 end 79 end
66 end 80 end
67 81
68 - def ldap_auth(login, password)  
69 - # Check user against LDAP backend if user is not authenticated  
70 - # Only check with valid login and password to prevent anonymous bind results  
71 - return nil unless ldap_conf.enabled && !login.blank? && !password.blank?  
72 -  
73 - ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)  
74 - ldap_user = ldap.bind_as(  
75 - filter: Net::LDAP::Filter.eq(ldap.uid, login),  
76 - size: 1,  
77 - password: password  
78 - )  
79 -  
80 - User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user  
81 - end  
82 -  
83 - def validate_get_request  
84 - validate_request(@request.params['service'])  
85 - end  
86 -  
87 - def validate_post_request  
88 - validate_request(File.basename(@request.path))  
89 - end  
90 -  
91 - def validate_request(service)  
92 - if service == 'git-upload-pack' 82 + def authorize_request(service)
  83 + case service
  84 + when 'git-upload-pack'
93 project.public || can?(user, :download_code, project) 85 project.public || can?(user, :download_code, project)
94 - elsif service == 'git-receive-pack'  
95 - action = if project.protected_branch?(current_ref) 86 + when'git-receive-pack'
  87 + action = if project.protected_branch?(ref)
96 :push_code_to_protected_branches 88 :push_code_to_protected_branches
97 else 89 else
98 :push_code 90 :push_code
@@ -104,49 +96,24 @@ module Grack @@ -104,49 +96,24 @@ module Grack
104 end 96 end
105 end 97 end
106 98
107 - def can?(object, action, subject)  
108 - abilities.allowed?(object, action, subject)  
109 - end  
110 -  
111 - def current_ref  
112 - if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/  
113 - input = Zlib::GzipReader.new(@request.body).read  
114 - else  
115 - input = @request.body.read  
116 - end  
117 - # Need to reset seek point  
118 - @request.body.rewind  
119 - /refs\/heads\/([\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last  
120 - end  
121 -  
122 def project 99 def project
123 - unless instance_variable_defined? :@project  
124 - # Find project by PATH_INFO from env  
125 - if m = /^\/([\w\.\/-]+)\.git/.match(@request.path_info).to_a  
126 - @project = Project.find_with_namespace(m.last)  
127 - end  
128 - end  
129 - return @project 100 + @project ||= project_by_path(@request.path_info)
130 end 101 end
131 102
132 - PLAIN_TYPE = {"Content-Type" => "text/plain"}  
133 -  
134 - def render_not_found  
135 - [404, PLAIN_TYPE, ["Not Found"]] 103 + def ref
  104 + @ref ||= parse_ref
136 end 105 end
137 106
138 - protected 107 + def parse_ref
  108 + input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
  109 + Zlib::GzipReader.new(@request.body).read
  110 + else
  111 + @request.body.read
  112 + end
139 113
140 - def abilities  
141 - @abilities ||= begin  
142 - abilities = Six.new  
143 - abilities << Ability  
144 - abilities  
145 - end  
146 - end  
147 -  
148 - def ldap_conf  
149 - @ldap_conf ||= Gitlab.config.ldap 114 + # Need to reset seek point
  115 + @request.body.rewind
  116 + /refs\/heads\/([\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last
150 end 117 end
151 - end# Auth  
152 -end# Grack 118 + end
  119 +end
lib/gitlab/backend/grack_helpers.rb 0 → 100644
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
  1 +module Grack
  2 + module Helpers
  3 + def project_by_path(path)
  4 + if m = /^\/([\w\.\/-]+)\.git/.match(path).to_a
  5 + path_with_namespace = m.last
  6 + path_with_namespace.gsub!(/.wiki$/, '')
  7 +
  8 + Project.find_with_namespace(path_with_namespace)
  9 + end
  10 + end
  11 +
  12 + def render_not_found
  13 + [404, {"Content-Type" => "text/plain"}, ["Not Found"]]
  14 + end
  15 +
  16 + def can?(object, action, subject)
  17 + abilities.allowed?(object, action, subject)
  18 + end
  19 +
  20 + def abilities
  21 + @abilities ||= begin
  22 + abilities = Six.new
  23 + abilities << Ability
  24 + abilities
  25 + end
  26 + end
  27 + end
  28 +end
lib/gitlab/backend/grack_ldap.rb 0 → 100644
@@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
  1 +require 'omniauth-ldap'
  2 +
  3 +module Grack
  4 + module LDAP
  5 + def ldap_auth(login, password)
  6 + # Check user against LDAP backend if user is not authenticated
  7 + # Only check with valid login and password to prevent anonymous bind results
  8 + return nil unless ldap_conf.enabled && !login.blank? && !password.blank?
  9 +
  10 + ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
  11 + ldap_user = ldap.bind_as(
  12 + filter: Net::LDAP::Filter.eq(ldap.uid, login),
  13 + size: 1,
  14 + password: password
  15 + )
  16 +
  17 + User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user
  18 + end
  19 +
  20 + def ldap_conf
  21 + @ldap_conf ||= Gitlab.config.ldap
  22 + end
  23 + end
  24 +end
lib/gitlab/blacklist.rb 0 → 100644
@@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
  1 +module Gitlab
  2 + module Blacklist
  3 + extend self
  4 +
  5 + def path
  6 + %w(admin dashboard groups help profile projects search public assets u s teams merge_requests issues users snippets )
  7 + end
  8 + end
  9 +end
spec/features/admin/admin_users_spec.rb
@@ -20,13 +20,10 @@ describe &quot;Admin::Users&quot; do @@ -20,13 +20,10 @@ describe &quot;Admin::Users&quot; do
20 20
21 describe "GET /admin/users/new" do 21 describe "GET /admin/users/new" do
22 before do 22 before do
23 - @password = "123ABC"  
24 visit new_admin_user_path 23 visit new_admin_user_path
25 fill_in "user_name", with: "Big Bang" 24 fill_in "user_name", with: "Big Bang"
26 fill_in "user_username", with: "bang" 25 fill_in "user_username", with: "bang"
27 fill_in "user_email", with: "bigbang@mail.com" 26 fill_in "user_email", with: "bigbang@mail.com"
28 - fill_in "user_password", with: @password  
29 - fill_in "user_password_confirmation", with: @password  
30 end 27 end
31 28
32 it "should create new user" do 29 it "should create new user" do
@@ -57,26 +54,13 @@ describe &quot;Admin::Users&quot; do @@ -57,26 +54,13 @@ describe &quot;Admin::Users&quot; do
57 end 54 end
58 55
59 it "should send valid email to user with email & password" do 56 it "should send valid email to user with email & password" do
60 - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)  
61 User.observers.enable :user_observer do 57 User.observers.enable :user_observer do
62 click_button "Create user" 58 click_button "Create user"
63 user = User.last 59 user = User.last
64 email = ActionMailer::Base.deliveries.last 60 email = ActionMailer::Base.deliveries.last
65 email.subject.should have_content("Account was created") 61 email.subject.should have_content("Account was created")
66 email.text_part.body.should have_content(user.email) 62 email.text_part.body.should have_content(user.email)
67 - email.text_part.body.should have_content(@password)  
68 - end  
69 - end  
70 -  
71 - it "should send valid email to user with email without password when signup is enabled" do  
72 - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)  
73 - User.observers.enable :user_observer do  
74 - click_button "Create user"  
75 - user = User.last  
76 - email = ActionMailer::Base.deliveries.last  
77 - email.subject.should have_content("Account was created")  
78 - email.text_part.body.should have_content(user.email)  
79 - email.text_part.body.should_not have_content(@password) 63 + email.text_part.body.should have_content('password')
80 end 64 end
81 end 65 end
82 end 66 end
spec/mailers/notify_spec.rb
@@ -15,7 +15,7 @@ describe Notify do @@ -15,7 +15,7 @@ describe Notify do
15 15
16 describe 'for new users, the email' do 16 describe 'for new users, the email' do
17 let(:example_site_path) { root_path } 17 let(:example_site_path) { root_path }
18 - let(:new_user) { create(:user, email: 'newguy@example.com') } 18 + let(:new_user) { create(:user, email: 'newguy@example.com', created_by_id: 1) }
19 19
20 subject { Notify.new_user_email(new_user.id, new_user.password) } 20 subject { Notify.new_user_email(new_user.id, new_user.password) }
21 21
@@ -32,8 +32,7 @@ describe Notify do @@ -32,8 +32,7 @@ describe Notify do
32 end 32 end
33 33
34 it 'contains the new user\'s password' do 34 it 'contains the new user\'s password' do
35 - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)  
36 - should have_body_text /#{new_user.password}/ 35 + should have_body_text /password/
37 end 36 end
38 37
39 it 'includes a link to the site' do 38 it 'includes a link to the site' do
@@ -61,8 +60,7 @@ describe Notify do @@ -61,8 +60,7 @@ describe Notify do
61 end 60 end
62 61
63 it 'should not contain the new user\'s password' do 62 it 'should not contain the new user\'s password' do
64 - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)  
65 - should_not have_body_text /#{new_user.password}/ 63 + should_not have_body_text /password/
66 end 64 end
67 65
68 it 'includes a link to the site' do 66 it 'includes a link to the site' do