Commit 76f2e065ea51f6364b1ca3dc0b1d29d6f52a4b04
Exists in
master
and in
4 other branches
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
CHANGELOG
@@ -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 |
Gemfile
@@ -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 |
Gemfile.lock
@@ -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) |
VERSION
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
app/assets/stylesheets/sections/events.scss
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 < ActionController::Base | @@ -156,4 +157,10 @@ class ApplicationController < 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 |
@@ -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 < ApplicationController | @@ -7,8 +7,12 @@ class SnippetsController < 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 < ApplicationController | @@ -98,4 +102,8 @@ class SnippetsController < 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 < ActiveRecord::Base | @@ -27,7 +27,7 @@ class Issue < 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 < ActiveRecord::Base | @@ -91,6 +91,15 @@ class MergeRequest < 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 < ActiveRecord::Base | @@ -27,6 +27,7 @@ class Namespace < 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 < ActiveRecord::Base | @@ -79,6 +79,7 @@ class Project < 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 < ActiveRecord::Base | @@ -92,7 +93,7 @@ class Project < 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 < ActiveRecord::Base | @@ -166,14 +167,6 @@ class Project < 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 < ActiveRecord::Base | @@ -420,6 +413,10 @@ class Project < 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 < ActiveRecord::Base | @@ -42,8 +42,11 @@ class User < 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 < ActiveRecord::Base | @@ -104,6 +107,7 @@ class User < 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 < ActiveRecord::Base | @@ -363,4 +367,8 @@ class User < 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 | - – user is a project owner | 128 | + |
129 | + .pull-right.light | ||
130 | + = tm.project_access_human | ||
131 | + |
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: | 5 | %span Sign in with: |
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 |
@@ -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/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 | - – | ||
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 | - – | ||
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 <%= @user.name %>! | @@ -3,10 +3,11 @@ Hi <%= @user.name %>! | ||
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) %> |
@@ -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
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
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
db/schema.rb
@@ -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 => 20130522141856) do | @@ -172,6 +172,7 @@ ActiveRecord::Schema.define(:version => 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 => 20130522141856) do | @@ -292,6 +293,8 @@ ActiveRecord::Schema.define(:version => 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 |
@@ -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 < Spinach::FeatureSteps | @@ -45,7 +45,7 @@ class ProjectActiveTab < 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 < Spinach::FeatureSteps | @@ -61,7 +61,7 @@ class ProjectActiveTab < 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 < Spinach::FeatureSteps | @@ -73,7 +73,7 @@ class ProjectActiveTab < 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 < Spinach::FeatureSteps | @@ -89,7 +89,7 @@ class ProjectActiveTab < 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 < Spinach::FeatureSteps | @@ -57,8 +57,8 @@ class ProjectMergeRequests < 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 < Spinach::FeatureSteps | @@ -50,12 +50,6 @@ class ProjectMilestones < 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 < Spinach::FeatureSteps | @@ -93,7 +93,7 @@ class Userteams < 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 < Spinach::FeatureSteps | @@ -121,7 +121,7 @@ class Userteams < 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 < Spinach::FeatureSteps | @@ -131,9 +131,7 @@ class Userteams < 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 < Spinach::FeatureSteps | @@ -145,10 +143,8 @@ class Userteams < 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 < Spinach::FeatureSteps | @@ -156,20 +152,8 @@ class Userteams < 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 |
@@ -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 |
@@ -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 |
spec/features/admin/admin_users_spec.rb
@@ -20,13 +20,10 @@ describe "Admin::Users" do | @@ -20,13 +20,10 @@ describe "Admin::Users" 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 "Admin::Users" do | @@ -57,26 +54,13 @@ describe "Admin::Users" 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 |