Commit 465499928835d3319e6838f43f04d5079a197850
Exists in
master
and in
4 other branches
Merge branch 'master' into relative_links_in_documentation
Showing
71 changed files
with
467 additions
and
330 deletions
Show diff stats
CHANGELOG
@@ -13,6 +13,7 @@ v 6.2.0 | @@ -13,6 +13,7 @@ v 6.2.0 | ||
13 | - Rake tasks for web hooks management (Jonhnny Weslley) | 13 | - Rake tasks for web hooks management (Jonhnny Weslley) |
14 | - Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov) | 14 | - Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov) |
15 | - API: Remove group | 15 | - API: Remove group |
16 | + - Avatar upload on profile page with a maximum of 200KB (Steven Thonus) | ||
16 | 17 | ||
17 | v 6.1.0 | 18 | v 6.1.0 |
18 | - Project specific IDs for issues, mr, milestones | 19 | - Project specific IDs for issues, mr, milestones |
Gemfile
@@ -16,6 +16,7 @@ gem "pg", group: :postgres | @@ -16,6 +16,7 @@ gem "pg", group: :postgres | ||
16 | 16 | ||
17 | # Auth | 17 | # Auth |
18 | gem "devise", '~> 2.2' | 18 | gem "devise", '~> 2.2' |
19 | +gem "devise-async" | ||
19 | gem 'omniauth', "~> 1.1.3" | 20 | gem 'omniauth', "~> 1.1.3" |
20 | gem 'omniauth-google-oauth2' | 21 | gem 'omniauth-google-oauth2' |
21 | gem 'omniauth-twitter' | 22 | gem 'omniauth-twitter' |
Gemfile.lock
@@ -106,6 +106,8 @@ GEM | @@ -106,6 +106,8 @@ GEM | ||
106 | orm_adapter (~> 0.1) | 106 | orm_adapter (~> 0.1) |
107 | railties (~> 3.1) | 107 | railties (~> 3.1) |
108 | warden (~> 1.2.1) | 108 | warden (~> 1.2.1) |
109 | + devise-async (0.8.0) | ||
110 | + devise (>= 2.2, < 3.2) | ||
109 | diff-lcs (1.2.4) | 111 | diff-lcs (1.2.4) |
110 | dotenv (0.8.0) | 112 | dotenv (0.8.0) |
111 | email_spec (1.4.0) | 113 | email_spec (1.4.0) |
@@ -564,6 +566,7 @@ DEPENDENCIES | @@ -564,6 +566,7 @@ DEPENDENCIES | ||
564 | d3_rails (~> 3.1.4) | 566 | d3_rails (~> 3.1.4) |
565 | database_cleaner | 567 | database_cleaner |
566 | devise (~> 2.2) | 568 | devise (~> 2.2) |
569 | + devise-async | ||
567 | email_spec | 570 | email_spec |
568 | enumerize | 571 | enumerize |
569 | factory_girl_rails | 572 | factory_girl_rails |
app/assets/javascripts/profile.js.coffee
@@ -16,3 +16,13 @@ $ -> | @@ -16,3 +16,13 @@ $ -> | ||
16 | 16 | ||
17 | $('.update-notifications').on 'ajax:complete', -> | 17 | $('.update-notifications').on 'ajax:complete', -> |
18 | $(this).find('.btn-save').enableButton() | 18 | $(this).find('.btn-save').enableButton() |
19 | + | ||
20 | + | ||
21 | + $('.js-choose-user-avatar-button').bind "click", -> | ||
22 | + form = $(this).closest("form") | ||
23 | + form.find(".js-user-avatar-input").click() | ||
24 | + | ||
25 | + $('.js-user-avatar-input').bind "change", -> | ||
26 | + form = $(this).closest("form") | ||
27 | + filename = $(this).val().replace(/^.*[\\\/]/, '') | ||
28 | + form.find(".js-avatar-filename").text(filename) |
app/assets/javascripts/users_select.js.coffee
1 | $ -> | 1 | $ -> |
2 | userFormatResult = (user) -> | 2 | userFormatResult = (user) -> |
3 | - avatar = gon.gravatar_url | ||
4 | - avatar = avatar.replace('%{hash}', md5(user.email)) | ||
5 | - avatar = avatar.replace('%{size}', '24') | ||
6 | - | 3 | + if user.avatar |
4 | + avatar = user.avatar.url | ||
5 | + else | ||
6 | + avatar = gon.gravatar_url | ||
7 | + avatar = avatar.replace('%{hash}', md5(user.email)) | ||
8 | + avatar = avatar.replace('%{size}', '24') | ||
7 | markup = "<div class='user-result'>" | 9 | markup = "<div class='user-result'>" |
8 | markup += "<div class='user-image'><img class='avatar s24' src='" + avatar + "'></div>" | 10 | markup += "<div class='user-image'><img class='avatar s24' src='" + avatar + "'></div>" |
9 | markup += "<div class='user-name'>" + user.name + "</div>" | 11 | markup += "<div class='user-name'>" + user.name + "</div>" |
app/assets/stylesheets/common.scss
@@ -270,27 +270,6 @@ li.note { | @@ -270,27 +270,6 @@ li.note { | ||
270 | } | 270 | } |
271 | } | 271 | } |
272 | 272 | ||
273 | -.oauth_select_holder { | ||
274 | - padding: 20px; | ||
275 | - img { | ||
276 | - padding: 5px; | ||
277 | - margin-right: 10px; | ||
278 | - } | ||
279 | - .active { | ||
280 | - img { | ||
281 | - border: 1px solid #ccc; | ||
282 | - background: $hover; | ||
283 | - @include border-radius(5px); | ||
284 | - } | ||
285 | - } | ||
286 | -} | ||
287 | - | ||
288 | -.btn-build-token { | ||
289 | - float: left; | ||
290 | - padding: 6px 20px; | ||
291 | - margin-right: 12px; | ||
292 | -} | ||
293 | - | ||
294 | .gitlab-promo { | 273 | .gitlab-promo { |
295 | a { | 274 | a { |
296 | color: #aaa; | 275 | color: #aaa; |
app/assets/stylesheets/gitlab_bootstrap/avatar.scss
@@ -19,4 +19,5 @@ | @@ -19,4 +19,5 @@ | ||
19 | &.s32 { width: 32px; height: 32px; margin-right: 10px; } | 19 | &.s32 { width: 32px; height: 32px; margin-right: 10px; } |
20 | &.s60 { width: 60px; height: 60px; margin-right: 12px; } | 20 | &.s60 { width: 60px; height: 60px; margin-right: 12px; } |
21 | &.s90 { width: 90px; height: 90px; margin-right: 15px; } | 21 | &.s90 { width: 90px; height: 90px; margin-right: 15px; } |
22 | + &.s160 { width: 160px; height: 160px; margin-right: 20px; } | ||
22 | } | 23 | } |
app/assets/stylesheets/sections/profile.scss
@@ -4,3 +4,41 @@ | @@ -4,3 +4,41 @@ | ||
4 | margin-bottom: 0; | 4 | margin-bottom: 0; |
5 | } | 5 | } |
6 | } | 6 | } |
7 | + | ||
8 | +.account-page { | ||
9 | + fieldset { | ||
10 | + margin-bottom: 15px; | ||
11 | + border-bottom: 1px dashed #ddd; | ||
12 | + padding-bottom: 15px; | ||
13 | + | ||
14 | + &:last-child { | ||
15 | + border: none; | ||
16 | + } | ||
17 | + | ||
18 | + legend { | ||
19 | + border: none; | ||
20 | + margin: 0; | ||
21 | + } | ||
22 | + } | ||
23 | +} | ||
24 | + | ||
25 | +.oauth_select_holder { | ||
26 | + img { | ||
27 | + padding: 2px; | ||
28 | + margin-right: 10px; | ||
29 | + } | ||
30 | + .active { | ||
31 | + img { | ||
32 | + border: 1px solid #4BD; | ||
33 | + background: $hover; | ||
34 | + @include border-radius(5px); | ||
35 | + } | ||
36 | + } | ||
37 | +} | ||
38 | + | ||
39 | +.btn-build-token { | ||
40 | + float: left; | ||
41 | + padding: 6px 20px; | ||
42 | + margin-right: 12px; | ||
43 | +} | ||
44 | + |
app/controllers/profiles/passwords_controller.rb
1 | class Profiles::PasswordsController < ApplicationController | 1 | class Profiles::PasswordsController < ApplicationController |
2 | - layout 'navless' | 2 | + layout :determine_layout |
3 | 3 | ||
4 | - skip_before_filter :check_password_expiration | 4 | + skip_before_filter :check_password_expiration, only: [:new, :create] |
5 | 5 | ||
6 | before_filter :set_user | 6 | before_filter :set_user |
7 | before_filter :set_title | 7 | before_filter :set_title |
8 | + before_filter :authorize_change_password! | ||
8 | 9 | ||
9 | def new | 10 | def new |
10 | end | 11 | end |
@@ -26,6 +27,32 @@ class Profiles::PasswordsController < ApplicationController | @@ -26,6 +27,32 @@ class Profiles::PasswordsController < ApplicationController | ||
26 | end | 27 | end |
27 | end | 28 | end |
28 | 29 | ||
30 | + def edit | ||
31 | + end | ||
32 | + | ||
33 | + def update | ||
34 | + password_attributes = params[:user].select do |key, value| | ||
35 | + %w(password password_confirmation).include?(key.to_s) | ||
36 | + end | ||
37 | + | ||
38 | + unless @user.valid_password?(params[:user][:current_password]) | ||
39 | + redirect_to edit_profile_password_path, alert: 'You must provide a valid current password' | ||
40 | + return | ||
41 | + end | ||
42 | + | ||
43 | + if @user.update_attributes(password_attributes) | ||
44 | + flash[:notice] = "Password was successfully updated. Please login with it" | ||
45 | + redirect_to new_user_session_path | ||
46 | + else | ||
47 | + render 'edit' | ||
48 | + end | ||
49 | + end | ||
50 | + | ||
51 | + def reset | ||
52 | + current_user.send_reset_password_instructions | ||
53 | + redirect_to edit_profile_password_path, notice: 'We sent you an email with reset password instructions' | ||
54 | + end | ||
55 | + | ||
29 | private | 56 | private |
30 | 57 | ||
31 | def set_user | 58 | def set_user |
@@ -35,4 +62,16 @@ class Profiles::PasswordsController < ApplicationController | @@ -35,4 +62,16 @@ class Profiles::PasswordsController < ApplicationController | ||
35 | def set_title | 62 | def set_title |
36 | @title = "New password" | 63 | @title = "New password" |
37 | end | 64 | end |
65 | + | ||
66 | + def determine_layout | ||
67 | + if [:new, :create].include?(action_name.to_sym) | ||
68 | + 'navless' | ||
69 | + else | ||
70 | + 'profile' | ||
71 | + end | ||
72 | + end | ||
73 | + | ||
74 | + def authorize_change_password! | ||
75 | + return render_404 if @user.ldap_user? | ||
76 | + end | ||
38 | end | 77 | end |
app/controllers/profiles_controller.rb
@@ -2,7 +2,6 @@ class ProfilesController < ApplicationController | @@ -2,7 +2,6 @@ class ProfilesController < ApplicationController | ||
2 | include ActionView::Helpers::SanitizeHelper | 2 | include ActionView::Helpers::SanitizeHelper |
3 | 3 | ||
4 | before_filter :user | 4 | before_filter :user |
5 | - before_filter :authorize_change_password!, only: :update_password | ||
6 | before_filter :authorize_change_username!, only: :update_username | 5 | before_filter :authorize_change_username!, only: :update_username |
7 | 6 | ||
8 | layout 'profile' | 7 | layout 'profile' |
@@ -13,9 +12,6 @@ class ProfilesController < ApplicationController | @@ -13,9 +12,6 @@ class ProfilesController < ApplicationController | ||
13 | def design | 12 | def design |
14 | end | 13 | end |
15 | 14 | ||
16 | - def account | ||
17 | - end | ||
18 | - | ||
19 | def update | 15 | def update |
20 | if @user.update_attributes(params[:user]) | 16 | if @user.update_attributes(params[:user]) |
21 | flash[:notice] = "Profile was successfully updated" | 17 | flash[:notice] = "Profile was successfully updated" |
@@ -29,33 +25,12 @@ class ProfilesController < ApplicationController | @@ -29,33 +25,12 @@ class ProfilesController < ApplicationController | ||
29 | end | 25 | end |
30 | end | 26 | end |
31 | 27 | ||
32 | - def token | ||
33 | - end | ||
34 | - | ||
35 | - def update_password | ||
36 | - password_attributes = params[:user].select do |key, value| | ||
37 | - %w(password password_confirmation).include?(key.to_s) | ||
38 | - end | ||
39 | - | ||
40 | - unless @user.valid_password?(params[:user][:current_password]) | ||
41 | - redirect_to account_profile_path, alert: 'You must provide a valid current password' | ||
42 | - return | ||
43 | - end | ||
44 | - | ||
45 | - if @user.update_attributes(password_attributes) | ||
46 | - flash[:notice] = "Password was successfully updated. Please login with it" | ||
47 | - redirect_to new_user_session_path | ||
48 | - else | ||
49 | - render 'account' | ||
50 | - end | ||
51 | - end | ||
52 | - | ||
53 | def reset_private_token | 28 | def reset_private_token |
54 | if current_user.reset_authentication_token! | 29 | if current_user.reset_authentication_token! |
55 | flash[:notice] = "Token was successfully updated" | 30 | flash[:notice] = "Token was successfully updated" |
56 | end | 31 | end |
57 | 32 | ||
58 | - redirect_to account_profile_path | 33 | + redirect_to profile_account_path |
59 | end | 34 | end |
60 | 35 | ||
61 | def history | 36 | def history |
@@ -76,10 +51,6 @@ class ProfilesController < ApplicationController | @@ -76,10 +51,6 @@ class ProfilesController < ApplicationController | ||
76 | @user = current_user | 51 | @user = current_user |
77 | end | 52 | end |
78 | 53 | ||
79 | - def authorize_change_password! | ||
80 | - return render_404 if @user.ldap_user? | ||
81 | - end | ||
82 | - | ||
83 | def authorize_change_username! | 54 | def authorize_change_username! |
84 | return render_404 unless @user.can_change_username? | 55 | return render_404 unless @user.can_change_username? |
85 | end | 56 | end |
app/helpers/application_helper.rb
@@ -49,6 +49,15 @@ module ApplicationHelper | @@ -49,6 +49,15 @@ module ApplicationHelper | ||
49 | args.any? { |v| v.to_s.downcase == action_name } | 49 | args.any? { |v| v.to_s.downcase == action_name } |
50 | end | 50 | end |
51 | 51 | ||
52 | + def avatar_icon(user_email = '', size = nil) | ||
53 | + user = User.find_by_email(user_email) | ||
54 | + if user && user.avatar.present? | ||
55 | + user.avatar.url | ||
56 | + else | ||
57 | + gravatar_icon(user_email, size) | ||
58 | + end | ||
59 | + end | ||
60 | + | ||
52 | def gravatar_icon(user_email = '', size = nil) | 61 | def gravatar_icon(user_email = '', size = nil) |
53 | size = 40 if size.nil? || size <= 0 | 62 | size = 40 if size.nil? || size <= 0 |
54 | 63 |
app/helpers/commits_helper.rb
@@ -108,7 +108,7 @@ module CommitsHelper | @@ -108,7 +108,7 @@ module CommitsHelper | ||
108 | source_name = commit.send "#{options[:source]}_name".to_sym | 108 | source_name = commit.send "#{options[:source]}_name".to_sym |
109 | source_email = commit.send "#{options[:source]}_email".to_sym | 109 | source_email = commit.send "#{options[:source]}_email".to_sym |
110 | text = if options[:avatar] | 110 | text = if options[:avatar] |
111 | - avatar = image_tag(gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") | 111 | + avatar = image_tag(avatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") |
112 | %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>} | 112 | %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>} |
113 | else | 113 | else |
114 | source_name | 114 | source_name |
app/helpers/projects_helper.rb
@@ -5,10 +5,10 @@ module ProjectsHelper | @@ -5,10 +5,10 @@ module ProjectsHelper | ||
5 | 5 | ||
6 | def link_to_project project | 6 | def link_to_project project |
7 | link_to project do | 7 | link_to project do |
8 | - title = content_tag(:strong, project.name) | 8 | + title = content_tag(:span, project.name, class: 'projet-name') |
9 | 9 | ||
10 | if project.namespace | 10 | if project.namespace |
11 | - namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'tiny') | 11 | + namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name') |
12 | title = namespace + title | 12 | title = namespace + title |
13 | end | 13 | end |
14 | 14 | ||
@@ -25,7 +25,7 @@ module ProjectsHelper | @@ -25,7 +25,7 @@ module ProjectsHelper | ||
25 | author_html = "" | 25 | author_html = "" |
26 | 26 | ||
27 | # Build avatar image tag | 27 | # Build avatar image tag |
28 | - author_html << image_tag(gravatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] | 28 | + author_html << image_tag(avatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] |
29 | 29 | ||
30 | # Build name span tag | 30 | # Build name span tag |
31 | author_html << content_tag(:span, sanitize(author.name), class: 'author') if opts[:name] | 31 | author_html << content_tag(:span, sanitize(author.name), class: 'author') if opts[:name] |
@@ -80,6 +80,18 @@ module ProjectsHelper | @@ -80,6 +80,18 @@ module ProjectsHelper | ||
80 | @project.milestones.active.order("id desc").all | 80 | @project.milestones.active.order("id desc").all |
81 | end | 81 | end |
82 | 82 | ||
83 | + def project_issues_trackers | ||
84 | + values = Project.issues_tracker.values.map do |tracker_key| | ||
85 | + if tracker_key.to_sym == :gitlab | ||
86 | + ['GitLab', tracker_key] | ||
87 | + else | ||
88 | + [Gitlab.config.issues_tracker[tracker_key]['title'] || tracker_key, tracker_key] | ||
89 | + end | ||
90 | + end | ||
91 | + | ||
92 | + options_for_select(values) | ||
93 | + end | ||
94 | + | ||
83 | private | 95 | private |
84 | 96 | ||
85 | def get_project_nav_tabs(project, current_user) | 97 | def get_project_nav_tabs(project, current_user) |
@@ -119,4 +131,13 @@ module ProjectsHelper | @@ -119,4 +131,13 @@ module ProjectsHelper | ||
119 | "your@email.com" | 131 | "your@email.com" |
120 | end | 132 | end |
121 | end | 133 | end |
134 | + | ||
135 | + def repository_size | ||
136 | + "#{@project.repository.size} MB" | ||
137 | + rescue | ||
138 | + # In order to prevent 500 error | ||
139 | + # when application cannot allocate memory | ||
140 | + # to calculate repo size - just show 'Unknown' | ||
141 | + 'unknown' | ||
142 | + end | ||
122 | end | 143 | end |
app/models/user.rb
@@ -38,13 +38,16 @@ | @@ -38,13 +38,16 @@ | ||
38 | # created_by_id :integer | 38 | # created_by_id :integer |
39 | # | 39 | # |
40 | 40 | ||
41 | +require 'carrierwave/orm/activerecord' | ||
42 | +require 'file_size_validator' | ||
43 | + | ||
41 | class User < ActiveRecord::Base | 44 | class User < ActiveRecord::Base |
42 | - devise :database_authenticatable, :token_authenticatable, :lockable, | 45 | + devise :database_authenticatable, :token_authenticatable, :lockable, :async, |
43 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :registerable | 46 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :registerable |
44 | 47 | ||
45 | attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username, | 48 | attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username, |
46 | :skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password, | 49 | :skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password, |
47 | - :extern_uid, :provider, :password_expires_at, | 50 | + :extern_uid, :provider, :password_expires_at, :avatar, |
48 | as: [:default, :admin] | 51 | as: [:default, :admin] |
49 | 52 | ||
50 | attr_accessible :projects_limit, :can_create_group, | 53 | attr_accessible :projects_limit, :can_create_group, |
@@ -113,6 +116,8 @@ class User < ActiveRecord::Base | @@ -113,6 +116,8 @@ class User < ActiveRecord::Base | ||
113 | 116 | ||
114 | validate :namespace_uniq, if: ->(user) { user.username_changed? } | 117 | validate :namespace_uniq, if: ->(user) { user.username_changed? } |
115 | 118 | ||
119 | + validates :avatar, file_size: { maximum: 100.kilobytes.to_i } | ||
120 | + | ||
116 | before_validation :generate_password, on: :create | 121 | before_validation :generate_password, on: :create |
117 | before_validation :sanitize_attrs | 122 | before_validation :sanitize_attrs |
118 | 123 | ||
@@ -150,6 +155,8 @@ class User < ActiveRecord::Base | @@ -150,6 +155,8 @@ class User < ActiveRecord::Base | ||
150 | end | 155 | end |
151 | end | 156 | end |
152 | 157 | ||
158 | + mount_uploader :avatar, AttachmentUploader | ||
159 | + | ||
153 | # Scopes | 160 | # Scopes |
154 | scope :admins, -> { where(admin: true) } | 161 | scope :admins, -> { where(admin: true) } |
155 | scope :blocked, -> { with_state(:blocked) } | 162 | scope :blocked, -> { with_state(:blocked) } |
@@ -391,4 +398,4 @@ class User < ActiveRecord::Base | @@ -391,4 +398,4 @@ class User < ActiveRecord::Base | ||
391 | 398 | ||
392 | self | 399 | self |
393 | end | 400 | end |
394 | -end | 401 | -end |
402 | +end | ||
395 | \ No newline at end of file | 403 | \ No newline at end of file |
app/views/admin/users/show.html.haml
@@ -20,7 +20,7 @@ | @@ -20,7 +20,7 @@ | ||
20 | .title | 20 | .title |
21 | Account: | 21 | Account: |
22 | .pull-right | 22 | .pull-right |
23 | - = image_tag gravatar_icon(@user.email, 32), class: "avatar s32" | 23 | + = image_tag avatar_icon(@user.email, 32), class: "avatar s32" |
24 | %ul.well-list | 24 | %ul.well-list |
25 | %li | 25 | %li |
26 | %span.light Name: | 26 | %span.light Name: |
@@ -117,11 +117,7 @@ | @@ -117,11 +117,7 @@ | ||
117 | - tm = project.team.find_tm(@user.id) | 117 | - tm = project.team.find_tm(@user.id) |
118 | %li.users_project | 118 | %li.users_project |
119 | = link_to admin_project_path(project), class: dom_class(project) do | 119 | = link_to admin_project_path(project), class: dom_class(project) do |
120 | - - if project.namespace | ||
121 | - = project.namespace.human_name | ||
122 | - \/ | ||
123 | - %strong.well-title | ||
124 | - = truncate(project.name, length: 45) | 120 | + = project.name_with_namespace |
125 | 121 | ||
126 | - if tm | 122 | - if tm |
127 | .pull-right | 123 | .pull-right |
app/views/dashboard/issues.atom.builder
@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | @@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | ||
12 | xml.link :href => project_issue_url(issue.project, issue) | 12 | xml.link :href => project_issue_url(issue.project, issue) |
13 | xml.title truncate(issue.title, :length => 80) | 13 | xml.title truncate(issue.title, :length => 80) |
14 | xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") | 14 | xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") |
15 | - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email) | 15 | + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email) |
16 | xml.author do |author| | 16 | xml.author do |author| |
17 | xml.name issue.author_name | 17 | xml.name issue.author_name |
18 | xml.email issue.author_email | 18 | xml.email issue.author_email |
app/views/dashboard/show.atom.builder
@@ -17,7 +17,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | @@ -17,7 +17,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | ||
17 | xml.link :href => event_link | 17 | xml.link :href => event_link |
18 | xml.title truncate(event_title, :length => 80) | 18 | xml.title truncate(event_title, :length => 80) |
19 | xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") | 19 | xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") |
20 | - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email) | 20 | + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email) |
21 | xml.author do |author| | 21 | xml.author do |author| |
22 | xml.name event.author_name | 22 | xml.name event.author_name |
23 | xml.email event.author_email | 23 | xml.email event.author_email |
app/views/events/_event.html.haml
@@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
4 | #{time_ago_in_words(event.created_at)} ago. | 4 | #{time_ago_in_words(event.created_at)} ago. |
5 | 5 | ||
6 | = cache event do | 6 | = cache event do |
7 | - = image_tag gravatar_icon(event.author_email), class: "avatar s24", alt:'' | 7 | + = image_tag avatar_icon(event.author_email), class: "avatar s24", alt:'' |
8 | 8 | ||
9 | - if event.push? | 9 | - if event.push? |
10 | = render "events/event/push", event: event | 10 | = render "events/event/push", event: event |
app/views/events/event/_push.html.haml
@@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
7 | = link_to project_commits_path(event.project, event.ref_name) do | 7 | = link_to project_commits_path(event.project, event.ref_name) do |
8 | %strong= truncate(event.ref_name, length: 30) | 8 | %strong= truncate(event.ref_name, length: 30) |
9 | at | 9 | at |
10 | - %strong= link_to_project event.project | 10 | + = link_to_project event.project |
11 | 11 | ||
12 | - if event.push_with_commits? | 12 | - if event.push_with_commits? |
13 | - project = event.project | 13 | - project = event.project |
app/views/groups/issues.atom.builder
@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | @@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | ||
12 | xml.link :href => project_issue_url(issue.project, issue) | 12 | xml.link :href => project_issue_url(issue.project, issue) |
13 | xml.title truncate(issue.title, :length => 80) | 13 | xml.title truncate(issue.title, :length => 80) |
14 | xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") | 14 | xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") |
15 | - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email) | 15 | + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email) |
16 | xml.author do |author| | 16 | xml.author do |author| |
17 | xml.name issue.author_name | 17 | xml.name issue.author_name |
18 | xml.email issue.author_email | 18 | xml.email issue.author_email |
app/views/groups/show.atom.builder
@@ -16,7 +16,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | @@ -16,7 +16,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | ||
16 | xml.link :href => event_link | 16 | xml.link :href => event_link |
17 | xml.title truncate(event_title, :length => 80) | 17 | xml.title truncate(event_title, :length => 80) |
18 | xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") | 18 | xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") |
19 | - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email) | 19 | + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email) |
20 | xml.author do |author| | 20 | xml.author do |author| |
21 | xml.name event.author_name | 21 | xml.name event.author_name |
22 | xml.email event.author_email | 22 | xml.email event.author_email |
app/views/layouts/_head_panel.html.haml
@@ -37,4 +37,4 @@ | @@ -37,4 +37,4 @@ | ||
37 | %i.icon-signout | 37 | %i.icon-signout |
38 | %li | 38 | %li |
39 | = link_to current_user, class: "profile-pic", id: 'profile-pic' do | 39 | = link_to current_user, class: "profile-pic", id: 'profile-pic' do |
40 | - = image_tag gravatar_icon(current_user.email, 26), alt: '' | 40 | + = image_tag avatar_icon(current_user.email, 26), alt: '' |
app/views/layouts/nav/_profile.html.haml
@@ -2,8 +2,11 @@ | @@ -2,8 +2,11 @@ | ||
2 | = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do | 2 | = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do |
3 | = link_to profile_path, title: "Profile" do | 3 | = link_to profile_path, title: "Profile" do |
4 | %i.icon-home | 4 | %i.icon-home |
5 | - = nav_link(path: 'profiles#account') do | ||
6 | - = link_to "Account", account_profile_path | 5 | + = nav_link(controller: :accounts) do |
6 | + = link_to "Account", profile_account_path | ||
7 | + - unless current_user.ldap_user? | ||
8 | + = nav_link(controller: :passwords) do | ||
9 | + = link_to "Password", edit_profile_password_path | ||
7 | = nav_link(controller: :notifications) do | 10 | = nav_link(controller: :notifications) do |
8 | = link_to "Notifications", profile_notifications_path | 11 | = link_to "Notifications", profile_notifications_path |
9 | = nav_link(controller: :keys) do | 12 | = nav_link(controller: :keys) do |
app/views/profiles/account.html.haml
@@ -1,141 +0,0 @@ | @@ -1,141 +0,0 @@ | ||
1 | -%h3.page-title | ||
2 | - Account settings | ||
3 | -%p.light | ||
4 | - You can change your password, username and private token here. | ||
5 | - - if current_user.ldap_user? | ||
6 | - Some options are unavailable for LDAP accounts | ||
7 | -%hr | ||
8 | - | ||
9 | - | ||
10 | -.row | ||
11 | - .span2 | ||
12 | - %ul.nav.nav-pills.nav-stacked.nav-stacked-menu | ||
13 | - %li.active | ||
14 | - = link_to '#tab-token', 'data-toggle' => 'tab' do | ||
15 | - Private Token | ||
16 | - %li | ||
17 | - = link_to '#tab-password', 'data-toggle' => 'tab' do | ||
18 | - Password | ||
19 | - | ||
20 | - - if show_profile_social_tab? | ||
21 | - %li | ||
22 | - = link_to '#tab-social', 'data-toggle' => 'tab' do | ||
23 | - Social Accounts | ||
24 | - | ||
25 | - - if show_profile_username_tab? | ||
26 | - %li | ||
27 | - = link_to '#tab-username', 'data-toggle' => 'tab' do | ||
28 | - Change Username | ||
29 | - | ||
30 | - - if show_profile_remove_tab? | ||
31 | - %li | ||
32 | - = link_to '#tab-remove', 'data-toggle' => 'tab' do | ||
33 | - Remove Account | ||
34 | - .span10 | ||
35 | - .tab-content | ||
36 | - .tab-pane.active#tab-token | ||
37 | - %fieldset.update-token | ||
38 | - %legend | ||
39 | - Private token | ||
40 | - %span.cred.pull-right | ||
41 | - keep it secret! | ||
42 | - %div | ||
43 | - = form_for @user, url: reset_private_token_profile_path, method: :put do |f| | ||
44 | - .data | ||
45 | - %p.slead | ||
46 | - Your private token is used to access application resources without authentication. | ||
47 | - %br | ||
48 | - It can be used for atom feeds or the API. | ||
49 | - %p.cgray | ||
50 | - - if current_user.private_token | ||
51 | - = text_field_tag "token", current_user.private_token, class: "input-xxlarge large_text input-xpadding" | ||
52 | - = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token" | ||
53 | - - else | ||
54 | - %span You don`t have one yet. Click generate to fix it. | ||
55 | - = f.submit 'Generate', class: "btn success btn-build-token" | ||
56 | - | ||
57 | - .tab-pane#tab-password | ||
58 | - %fieldset.update-password | ||
59 | - %legend Password | ||
60 | - - if current_user.ldap_user? | ||
61 | - %h3.nothing_here_message Not available for LDAP user | ||
62 | - - else | ||
63 | - = form_for @user, url: update_password_profile_path, method: :put do |f| | ||
64 | - %div | ||
65 | - %p.slead | ||
66 | - You must provide current password in order to change it. | ||
67 | - %br | ||
68 | - After a successful password update you will be redirected to login page where you should login with your new password | ||
69 | - -if @user.errors.any? | ||
70 | - .alert.alert-error | ||
71 | - %ul | ||
72 | - - @user.errors.full_messages.each do |msg| | ||
73 | - %li= msg | ||
74 | - .control-group | ||
75 | - = f.label :current_password, class: 'cgreen' | ||
76 | - .controls= f.password_field :current_password, required: true | ||
77 | - .control-group | ||
78 | - = f.label :password, 'New password' | ||
79 | - .controls= f.password_field :password, required: true | ||
80 | - .control-group | ||
81 | - = f.label :password_confirmation | ||
82 | - .controls | ||
83 | - = f.password_field :password_confirmation, required: true | ||
84 | - .control-group | ||
85 | - .controls | ||
86 | - = f.submit 'Save password', class: "btn btn-save" | ||
87 | - | ||
88 | - - if show_profile_social_tab? | ||
89 | - .tab-pane#tab-social | ||
90 | - %fieldset | ||
91 | - %legend Social Accounts | ||
92 | - .oauth_select_holder | ||
93 | - %p.hint Tip: Click on icon to activate signin with one of the following services | ||
94 | - - enabled_social_providers.each do |provider| | ||
95 | - %span{class: oauth_active_class(provider) } | ||
96 | - = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) | ||
97 | - | ||
98 | - - if show_profile_username_tab? | ||
99 | - .tab-pane#tab-username | ||
100 | - %fieldset.update-username | ||
101 | - %legend | ||
102 | - Username | ||
103 | - %small.cred.pull-right | ||
104 | - Changing your username can have unintended side effects! | ||
105 | - = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| | ||
106 | - %div | ||
107 | - .control-group | ||
108 | - = f.label :username | ||
109 | - .controls | ||
110 | - = f.text_field :username, required: true | ||
111 | - | ||
112 | - %span.loading-gif.hide= image_tag "ajax_loader.gif" | ||
113 | - %span.update-success.cgreen.hide | ||
114 | - %i.icon-ok | ||
115 | - Saved | ||
116 | - %span.update-failed.cred.hide | ||
117 | - %i.icon-remove | ||
118 | - Failed | ||
119 | - %ul.cred | ||
120 | - %li This will change the web URL for personal projects. | ||
121 | - %li This will change the git path to repositories for personal projects. | ||
122 | - .controls | ||
123 | - = f.submit 'Save username', class: "btn btn-save" | ||
124 | - | ||
125 | - - if show_profile_remove_tab? | ||
126 | - .tab-pane#tab-remove | ||
127 | - %fieldset.remove-account | ||
128 | - %legend | ||
129 | - Remove account | ||
130 | - %div | ||
131 | - %p Deleting an account has the following effects: | ||
132 | - %ul | ||
133 | - %li All user content like authored issues, snippets, comments will be removed | ||
134 | - - rp = current_user.personal_projects.count | ||
135 | - - unless rp.zero? | ||
136 | - %li #{pluralize rp, 'personal project'} will be removed and cannot be restored | ||
137 | - - if current_user.solo_owned_groups.present? | ||
138 | - %li | ||
139 | - Next groups will be abandoned. You should transfer or remove them: | ||
140 | - %strong #{current_user.solo_owned_groups.map(&:name).join(', ')} | ||
141 | - = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove" |
@@ -0,0 +1,73 @@ | @@ -0,0 +1,73 @@ | ||
1 | +%h3.page-title | ||
2 | + Account settings | ||
3 | +%p.light | ||
4 | + You can change your username and private token here. | ||
5 | + - if current_user.ldap_user? | ||
6 | + Some options are unavailable for LDAP accounts | ||
7 | +%hr | ||
8 | + | ||
9 | + | ||
10 | +.account-page | ||
11 | + %fieldset.update-token | ||
12 | + %legend | ||
13 | + Private token | ||
14 | + %div | ||
15 | + = form_for @user, url: reset_private_token_profile_path, method: :put do |f| | ||
16 | + .data | ||
17 | + %p | ||
18 | + Your private token is used to access application resources without authentication. | ||
19 | + %br | ||
20 | + It can be used for atom feeds or the API. | ||
21 | + %span.cred | ||
22 | + Keep it secret! | ||
23 | + | ||
24 | + %p.cgray | ||
25 | + - if current_user.private_token | ||
26 | + = text_field_tag "token", current_user.private_token, class: "input-xlarge input-xpadding pull-left" | ||
27 | + = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token prepend-left-10" | ||
28 | + - else | ||
29 | + %span You don`t have one yet. Click generate to fix it. | ||
30 | + = f.submit 'Generate', class: "btn success btn-build-token" | ||
31 | + | ||
32 | + | ||
33 | + - if show_profile_social_tab? | ||
34 | + %fieldset | ||
35 | + %legend Social Accounts | ||
36 | + .oauth_select_holder.append-bottom-10 | ||
37 | + %p Click on icon to activate signin with one of the following services | ||
38 | + - enabled_social_providers.each do |provider| | ||
39 | + %span{class: oauth_active_class(provider) } | ||
40 | + = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) | ||
41 | + | ||
42 | + - if show_profile_username_tab? | ||
43 | + %fieldset.update-username | ||
44 | + %legend | ||
45 | + Username | ||
46 | + = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| | ||
47 | + %p | ||
48 | + Changing your username will change path to all personl projects! | ||
49 | + %div | ||
50 | + = f.text_field :username, required: true, class: 'input-xlarge input-xpadding' | ||
51 | + | ||
52 | + %span.loading-gif.hide= image_tag "ajax_loader.gif" | ||
53 | + %p.light | ||
54 | + = user_url(@user) | ||
55 | + %div | ||
56 | + = f.submit 'Save username', class: "btn btn-save" | ||
57 | + | ||
58 | + - if show_profile_remove_tab? | ||
59 | + %fieldset.remove-account | ||
60 | + %legend | ||
61 | + Remove account | ||
62 | + %div | ||
63 | + %p Deleting an account has the following effects: | ||
64 | + %ul | ||
65 | + %li All user content like authored issues, snippets, comments will be removed | ||
66 | + - rp = current_user.personal_projects.count | ||
67 | + - unless rp.zero? | ||
68 | + %li #{pluralize rp, 'personal project'} will be removed and cannot be restored | ||
69 | + - if current_user.solo_owned_groups.present? | ||
70 | + %li | ||
71 | + Next groups will be abandoned. You should transfer or remove them: | ||
72 | + %strong #{current_user.solo_owned_groups.map(&:name).join(', ')} | ||
73 | + = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove" |
@@ -0,0 +1,32 @@ | @@ -0,0 +1,32 @@ | ||
1 | +%h3.page-title Password | ||
2 | +%p.light | ||
3 | + Change your password or recover your current one. | ||
4 | +%hr | ||
5 | +.update-password | ||
6 | + = form_for @user, url: profile_password_path, method: :put do |f| | ||
7 | + %div | ||
8 | + %p.slead | ||
9 | + You must provide current password in order to change it. | ||
10 | + %br | ||
11 | + After a successful password update you will be redirected to login page where you should login with your new password | ||
12 | + -if @user.errors.any? | ||
13 | + .alert.alert-error | ||
14 | + %ul | ||
15 | + - @user.errors.full_messages.each do |msg| | ||
16 | + %li= msg | ||
17 | + .control-group | ||
18 | + = f.label :current_password | ||
19 | + .controls | ||
20 | + = f.password_field :current_password, required: true | ||
21 | + %div | ||
22 | + = link_to "Forgot your password?", reset_profile_password_path, method: :put | ||
23 | + | ||
24 | + .control-group | ||
25 | + = f.label :password, 'New password' | ||
26 | + .controls= f.password_field :password, required: true | ||
27 | + .control-group | ||
28 | + = f.label :password_confirmation | ||
29 | + .controls | ||
30 | + = f.password_field :password_confirmation, required: true | ||
31 | + .form-actions | ||
32 | + = f.submit 'Save password', class: "btn btn-save" |
app/views/profiles/show.html.haml
1 | -= image_tag gravatar_icon(@user.email, 60), alt: '', class: 'avatar s60' | ||
2 | %h3.page-title | 1 | %h3.page-title |
3 | - = @user.name | ||
4 | - %br | ||
5 | - %small | ||
6 | - = @user.email | ||
7 | - | ||
8 | - .pull-right | ||
9 | - = link_to destroy_user_session_path, class: "logout", method: :delete do | ||
10 | - %small | ||
11 | - %i.icon-signout | ||
12 | - Logout | 2 | + Profile settings |
3 | +%p.light | ||
4 | + This information appears on your profile. | ||
5 | + - if current_user.ldap_user? | ||
6 | + Some options are unavailable for LDAP accounts | ||
13 | %hr | 7 | %hr |
14 | 8 | ||
15 | -= form_for @user, url: profile_path, method: :put, html: { class: "edit_user form-horizontal" } do |f| | 9 | + |
10 | + | ||
11 | += form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit_user form-horizontal" } do |f| | ||
16 | -if @user.errors.any? | 12 | -if @user.errors.any? |
17 | %div.alert.alert-error | 13 | %div.alert.alert-error |
18 | %ul | 14 | %ul |
@@ -29,7 +25,7 @@ | @@ -29,7 +25,7 @@ | ||
29 | = f.label :email, class: "control-label" | 25 | = f.label :email, class: "control-label" |
30 | .controls | 26 | .controls |
31 | = f.text_field :email, class: "input-xlarge", required: true | 27 | = f.text_field :email, class: "input-xlarge", required: true |
32 | - %span.help-block We also use email for avatar detection. | 28 | + %span.help-block We also use email for avatar detection if no avatar is uploaded. |
33 | .control-group | 29 | .control-group |
34 | = f.label :skype, class: "control-label" | 30 | = f.label :skype, class: "control-label" |
35 | .controls= f.text_field :skype, class: "input-xlarge" | 31 | .controls= f.text_field :skype, class: "input-xlarge" |
@@ -46,45 +42,23 @@ | @@ -46,45 +42,23 @@ | ||
46 | %span.help-block Tell us about yourself in fewer than 250 characters. | 42 | %span.help-block Tell us about yourself in fewer than 250 characters. |
47 | 43 | ||
48 | .span5.pull-right | 44 | .span5.pull-right |
49 | - %fieldset.tips | ||
50 | - %legend Tips: | ||
51 | - %ul | ||
52 | - %li | ||
53 | - %p You can change your password on the Account page | ||
54 | - - if Gitlab.config.gravatar.enabled | ||
55 | - %li | ||
56 | - %p You can change your avatar at #{link_to "gravatar.com", "http://gravatar.com"} | ||
57 | - | ||
58 | - - if Gitlab.config.omniauth.enabled && @user.provider? | ||
59 | - %li | ||
60 | - %p | ||
61 | - You can login through #{@user.provider.titleize}! | ||
62 | - = link_to "click here to change", account_profile_path | ||
63 | - - if current_user.can_create_group? | ||
64 | - %li | ||
65 | - %p | ||
66 | - Need a group for several dependent projects? | ||
67 | - = link_to new_group_path, class: "btn btn-tiny" do | ||
68 | - Create a group | ||
69 | - - unless current_user.projects_limit_left > 100 | ||
70 | - %fieldset | ||
71 | - %legend | ||
72 | - Personal projects: | ||
73 | - %small.pull-right | ||
74 | - %span= current_user.personal_projects.count | ||
75 | - of | ||
76 | - %span= current_user.projects_limit | ||
77 | - .padded | ||
78 | - .progress | ||
79 | - .bar{style: "width: #{current_user.projects_limit_percent}%;"} | 45 | + .light-well |
46 | + = image_tag avatar_icon(@user.email, 160), alt: '', class: 'avatar s160' | ||
80 | 47 | ||
81 | - %fieldset | ||
82 | - %legend | ||
83 | - SSH public keys: | ||
84 | - %span.pull-right | ||
85 | - = link_to pluralize(current_user.keys.count, 'key'), profile_keys_path | ||
86 | - .padded | ||
87 | - = link_to "Add Public Key", new_profile_key_path, class: "btn btn-small" | 48 | + .clearfix |
49 | + .profile-avatar-form-option | ||
50 | + %p.light | ||
51 | + You can upload an avatar here | ||
52 | + %br | ||
53 | + or change it at #{link_to "gravatar.com", "http://gravatar.com"} | ||
54 | + %hr | ||
55 | + %a.choose-btn.btn.btn-small.js-choose-user-avatar-button | ||
56 | + %i.icon-paper-clip | ||
57 | + %span Choose File ... | ||
58 | + | ||
59 | + %span.file_name.js-avatar-filename File name... | ||
60 | + = f.file_field :avatar, class: "js-user-avatar-input hide" | ||
61 | + %span.help-block The maximum file size allowed is 200KB. | ||
88 | 62 | ||
89 | .form-actions | 63 | .form-actions |
90 | = f.submit 'Save changes', class: "btn btn-save" | 64 | = f.submit 'Save changes', class: "btn btn-save" |
app/views/profiles/update_username.js.haml
1 | - if @user.valid? | 1 | - if @user.valid? |
2 | :plain | 2 | :plain |
3 | - $('.update-username .update-success').show(); | 3 | + new Flash("Username sucessfully changed", "notice") |
4 | - else | 4 | - else |
5 | :plain | 5 | :plain |
6 | - $('.update-username .update-failed').show(); | 6 | + new Flash("Username change failed - #{@user.errors.full_messages.first}", "alert") |
app/views/projects/branches/_branch.html.haml
@@ -24,7 +24,7 @@ | @@ -24,7 +24,7 @@ | ||
24 | %p | 24 | %p |
25 | = link_to project_commit_path(@project, commit.id), class: 'commit_short_id' do | 25 | = link_to project_commit_path(@project, commit.id), class: 'commit_short_id' do |
26 | = commit.short_id | 26 | = commit.short_id |
27 | - = image_tag gravatar_icon(commit.author_email), class: "avatar s16", alt: '' | 27 | + = image_tag avatar_icon(commit.author_email), class: "avatar s16", alt: '' |
28 | %span.light | 28 | %span.light |
29 | = gfm escape_once(truncate(commit.title, length: 40)) | 29 | = gfm escape_once(truncate(commit.title, length: 40)) |
30 | %span | 30 | %span |
app/views/projects/commits/show.atom.builder
@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | @@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | ||
12 | xml.link :href => project_commit_url(@project, :id => commit.id) | 12 | xml.link :href => project_commit_url(@project, :id => commit.id) |
13 | xml.title truncate(commit.title, :length => 80) | 13 | xml.title truncate(commit.title, :length => 80) |
14 | xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") | 14 | xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") |
15 | - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(commit.author_email) | 15 | + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(commit.author_email) |
16 | xml.author do |author| | 16 | xml.author do |author| |
17 | xml.name commit.author_name | 17 | xml.name commit.author_name |
18 | xml.email commit.author_email | 18 | xml.email commit.author_email |
app/views/projects/edit.html.haml
@@ -67,7 +67,7 @@ | @@ -67,7 +67,7 @@ | ||
67 | - if Project.issues_tracker.values.count > 1 | 67 | - if Project.issues_tracker.values.count > 1 |
68 | .control-group | 68 | .control-group |
69 | = f.label :issues_tracker, "Issues tracker", class: 'control-label' | 69 | = f.label :issues_tracker, "Issues tracker", class: 'control-label' |
70 | - .controls= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled }) | 70 | + .controls= f.select(:issues_tracker, project_issues_trackers, {}, { disabled: !@project.issues_enabled }) |
71 | 71 | ||
72 | .control-group | 72 | .control-group |
73 | = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label' | 73 | = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label' |
app/views/projects/issues/_issues.html.haml
@@ -52,7 +52,7 @@ | @@ -52,7 +52,7 @@ | ||
52 | - @project.team.members.sort_by(&:name).each do |user| | 52 | - @project.team.members.sort_by(&:name).each do |user| |
53 | %li | 53 | %li |
54 | = link_to project_filter_path(assignee_id: user.id) do | 54 | = link_to project_filter_path(assignee_id: user.id) do |
55 | - = image_tag gravatar_icon(user.email), class: "avatar s16", alt: '' | 55 | + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' |
56 | = user.name | 56 | = user.name |
57 | 57 | ||
58 | .dropdown.inline.prepend-left-10 | 58 | .dropdown.inline.prepend-left-10 |
app/views/projects/issues/index.atom.builder
@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | @@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear | ||
12 | xml.link :href => project_issue_url(@project, issue) | 12 | xml.link :href => project_issue_url(@project, issue) |
13 | xml.title truncate(issue.title, :length => 80) | 13 | xml.title truncate(issue.title, :length => 80) |
14 | xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") | 14 | xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") |
15 | - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email) | 15 | + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email) |
16 | xml.author do |author| | 16 | xml.author do |author| |
17 | xml.name issue.author_name | 17 | xml.name issue.author_name |
18 | xml.email issue.author_email | 18 | xml.email issue.author_email |
app/views/projects/merge_requests/index.html.haml
@@ -35,7 +35,7 @@ | @@ -35,7 +35,7 @@ | ||
35 | - @project.team.members.sort_by(&:name).each do |user| | 35 | - @project.team.members.sort_by(&:name).each do |user| |
36 | %li | 36 | %li |
37 | = link_to project_filter_path(assignee_id: user.id) do | 37 | = link_to project_filter_path(assignee_id: user.id) do |
38 | - = image_tag gravatar_icon(user.email), class: "avatar s16", alt: '' | 38 | + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' |
39 | = user.name | 39 | = user.name |
40 | 40 | ||
41 | .dropdown.inline.prepend-left-10 | 41 | .dropdown.inline.prepend-left-10 |
app/views/projects/milestones/_issues.html.haml
@@ -8,4 +8,4 @@ | @@ -8,4 +8,4 @@ | ||
8 | = link_to_gfm truncate(issue.title, length: 40), [@project, issue] | 8 | = link_to_gfm truncate(issue.title, length: 40), [@project, issue] |
9 | - if issue.assignee | 9 | - if issue.assignee |
10 | .pull-right | 10 | .pull-right |
11 | - = image_tag gravatar_icon(issue.assignee.email, 16), class: "avatar s16" | 11 | + = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16" |
app/views/projects/milestones/show.html.haml
@@ -99,7 +99,7 @@ | @@ -99,7 +99,7 @@ | ||
99 | - @users.each do |user| | 99 | - @users.each do |user| |
100 | %li | 100 | %li |
101 | = link_to user, title: user.name, class: "dark" do | 101 | = link_to user, title: user.name, class: "dark" do |
102 | - = image_tag gravatar_icon(user.email, 32), class: "avatar s32" | 102 | + = image_tag avatar_icon(user.email, 32), class: "avatar s32" |
103 | %strong= truncate(user.name, lenght: 40) | 103 | %strong= truncate(user.name, lenght: 40) |
104 | %br | 104 | %br |
105 | %small.cgray= user.username | 105 | %small.cgray= user.username |
app/views/projects/network/show.json.erb
@@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
9 | author: { | 9 | author: { |
10 | name: c.author_name, | 10 | name: c.author_name, |
11 | email: c.author_email, | 11 | email: c.author_email, |
12 | - icon: gravatar_icon(c.author_email, 20) | 12 | + icon: avatar_icon(c.author_email, 20) |
13 | }, | 13 | }, |
14 | time: c.time, | 14 | time: c.time, |
15 | space: c.spaces.first, | 15 | space: c.spaces.first, |
app/views/projects/notes/_discussion.html.haml
@@ -8,7 +8,7 @@ | @@ -8,7 +8,7 @@ | ||
8 | = link_to "javascript:;", class: "js-details-target turn-off js-toggler-target" do | 8 | = link_to "javascript:;", class: "js-details-target turn-off js-toggler-target" do |
9 | %i.icon-eye-open | 9 | %i.icon-eye-open |
10 | Show discussion | 10 | Show discussion |
11 | - = image_tag gravatar_icon(note.author_email), class: "avatar s32" | 11 | + = image_tag avatar_icon(note.author_email), class: "avatar s32" |
12 | %div | 12 | %div |
13 | = link_to_member(@project, note.author, avatar: false) | 13 | = link_to_member(@project, note.author, avatar: false) |
14 | - if note.for_merge_request? | 14 | - if note.for_merge_request? |
app/views/projects/notes/_note.html.haml
@@ -13,7 +13,7 @@ | @@ -13,7 +13,7 @@ | ||
13 | = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, confirm: 'Are you sure you want to remove this comment?', remote: true, class: "danger js-note-delete" do | 13 | = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, confirm: 'Are you sure you want to remove this comment?', remote: true, class: "danger js-note-delete" do |
14 | %i.icon-trash.cred | 14 | %i.icon-trash.cred |
15 | Remove | 15 | Remove |
16 | - = image_tag gravatar_icon(note.author_email), class: "avatar s32" | 16 | + = image_tag avatar_icon(note.author_email), class: "avatar s32" |
17 | = link_to_member(@project, note.author, avatar: false) | 17 | = link_to_member(@project, note.author, avatar: false) |
18 | %span.note-last-update | 18 | %span.note-last-update |
19 | = note_timestamp(note) | 19 | = note_timestamp(note) |
app/views/projects/repositories/_feed.html.haml
@@ -11,7 +11,7 @@ | @@ -11,7 +11,7 @@ | ||
11 | %div | 11 | %div |
12 | = link_to project_commits_path(@project, commit.id) do | 12 | = link_to project_commits_path(@project, commit.id) do |
13 | %code= commit.short_id | 13 | %code= commit.short_id |
14 | - = image_tag gravatar_icon(commit.author_email), class: "", width: 16, alt: '' | 14 | + = image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: '' |
15 | = gfm escape_once(truncate(commit.title, length: 40)) | 15 | = gfm escape_once(truncate(commit.title, length: 40)) |
16 | %td | 16 | %td |
17 | %span.pull-right.cgray | 17 | %span.pull-right.cgray |
app/views/projects/repositories/stats.html.haml
@@ -19,7 +19,7 @@ | @@ -19,7 +19,7 @@ | ||
19 | %ol.styled | 19 | %ol.styled |
20 | - @stats.authors[0...50].each do |author| | 20 | - @stats.authors[0...50].each do |author| |
21 | %li | 21 | %li |
22 | - = image_tag gravatar_icon(author.email, 16), class: 'avatar s16', alt: '' | 22 | + = image_tag avatar_icon(author.email, 16), class: 'avatar s16', alt: '' |
23 | = author.name | 23 | = author.name |
24 | %small.light= author.email | 24 | %small.light= author.email |
25 | .pull-right | 25 | .pull-right |
app/views/projects/show.html.haml
@@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
17 | %p | 17 | %p |
18 | %p | 18 | %p |
19 | %span.light Repo size is | 19 | %span.light Repo size is |
20 | - #{@project.repository.size} MB | 20 | + = repository_size |
21 | %p | 21 | %p |
22 | %span.light Created at | 22 | %span.light Created at |
23 | #{@project.created_at.stamp('Aug 22, 2013')} | 23 | #{@project.created_at.stamp('Aug 22, 2013')} |
app/views/projects/snippets/_snippet.html.haml
@@ -16,6 +16,6 @@ | @@ -16,6 +16,6 @@ | ||
16 | = "##{snippet.id}" | 16 | = "##{snippet.id}" |
17 | %span | 17 | %span |
18 | by | 18 | by |
19 | - = image_tag gravatar_icon(snippet.author_email), class: "avatar avatar-inline s16" | 19 | + = image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16" |
20 | = snippet.author_name | 20 | = snippet.author_name |
21 | %span.light #{time_ago_in_words(snippet.created_at)} ago | 21 | %span.light #{time_ago_in_words(snippet.created_at)} ago |
app/views/projects/snippets/show.html.haml
@@ -5,7 +5,7 @@ | @@ -5,7 +5,7 @@ | ||
5 | = "##{@snippet.id}" | 5 | = "##{@snippet.id}" |
6 | %span.light | 6 | %span.light |
7 | by | 7 | by |
8 | - = image_tag gravatar_icon(@snippet.author_email), class: "avatar avatar-inline s16" | 8 | + = image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16" |
9 | = @snippet.author_name | 9 | = @snippet.author_name |
10 | %div= render 'projects/snippets/blob' | 10 | %div= render 'projects/snippets/blob' |
11 | %div#notes= render "projects/notes/notes_with_form" | 11 | %div#notes= render "projects/notes/notes_with_form" |
app/views/projects/team_members/_team_member.html.haml
@@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
9 | | 9 | |
10 | = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from team' do | 10 | = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from team' do |
11 | %i.icon-minus.icon-white | 11 | %i.icon-minus.icon-white |
12 | - = image_tag gravatar_icon(user.email, 32), class: "avatar s32" | 12 | + = image_tag avatar_icon(user.email, 32), class: "avatar s32" |
13 | %p | 13 | %p |
14 | %strong= user.name | 14 | %strong= user.name |
15 | %span.cgray= user.username | 15 | %span.cgray= user.username |
app/views/snippets/_snippet.html.haml
@@ -18,6 +18,6 @@ | @@ -18,6 +18,6 @@ | ||
18 | %span | 18 | %span |
19 | by | 19 | by |
20 | = link_to user_snippets_path(snippet.author) do | 20 | = link_to user_snippets_path(snippet.author) do |
21 | - = image_tag gravatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: '' | 21 | + = image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: '' |
22 | = snippet.author_name | 22 | = snippet.author_name |
23 | %span.light #{time_ago_in_words(snippet.created_at)} ago | 23 | %span.light #{time_ago_in_words(snippet.created_at)} ago |
app/views/snippets/show.html.haml
@@ -17,7 +17,7 @@ | @@ -17,7 +17,7 @@ | ||
17 | %span.light | 17 | %span.light |
18 | by | 18 | by |
19 | = link_to user_snippets_path(@snippet.author) do | 19 | = link_to user_snippets_path(@snippet.author) do |
20 | - = image_tag gravatar_icon(@snippet.author_email), class: "avatar avatar-inline s16" | 20 | + = image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16" |
21 | = @snippet.author_name | 21 | = @snippet.author_name |
22 | 22 | ||
23 | .back-link | 23 | .back-link |
app/views/snippets/user_index.html.haml
app/views/users/_projects.html.haml
@@ -3,9 +3,4 @@ | @@ -3,9 +3,4 @@ | ||
3 | %ul.well-list | 3 | %ul.well-list |
4 | - @projects.each do |project| | 4 | - @projects.each do |project| |
5 | %li | 5 | %li |
6 | - = link_to project_path(project), class: dom_class(project) do | ||
7 | - - if project.namespace | ||
8 | - = project.namespace.human_name | ||
9 | - \/ | ||
10 | - %strong.well-title | ||
11 | - = truncate(project.name, length: 45) | 6 | + = link_to_project project |
app/views/users/show.html.haml
1 | .row | 1 | .row |
2 | .span8 | 2 | .span8 |
3 | %h3.page-title | 3 | %h3.page-title |
4 | - = image_tag gravatar_icon(@user.email, 90), class: "avatar s90", alt: '' | 4 | + = image_tag avatar_icon(@user.email, 90), class: "avatar s90", alt: '' |
5 | = @user.name | 5 | = @user.name |
6 | - if @user == current_user | 6 | - if @user == current_user |
7 | .pull-right | 7 | .pull-right |
app/views/users_groups/_users_group.html.haml
1 | - user = member.user | 1 | - user = member.user |
2 | - return unless user | 2 | - return unless user |
3 | %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} | 3 | %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} |
4 | - = image_tag gravatar_icon(user.email, 16), class: "avatar s16" | 4 | + = image_tag avatar_icon(user.email, 16), class: "avatar s16" |
5 | %strong= user.name | 5 | %strong= user.name |
6 | %span.cgray= user.username | 6 | %span.cgray= user.username |
7 | - if user == current_user | 7 | - if user == current_user |
config/gitlab.yml.example
@@ -73,6 +73,7 @@ production: &base | @@ -73,6 +73,7 @@ production: &base | ||
73 | ## External issues trackers | 73 | ## External issues trackers |
74 | issues_tracker: | 74 | issues_tracker: |
75 | # redmine: | 75 | # redmine: |
76 | + # title: "Redmine" | ||
76 | # ## If not nil, link 'Issues' on project page will be replaced with this | 77 | # ## If not nil, link 'Issues' on project page will be replaced with this |
77 | # ## Use placeholders: | 78 | # ## Use placeholders: |
78 | # ## :project_id - GitLab project identifier | 79 | # ## :project_id - GitLab project identifier |
@@ -93,6 +94,7 @@ production: &base | @@ -93,6 +94,7 @@ production: &base | ||
93 | # new_issue_url: "http://redmine.sample/projects/:issues_tracker_id/issues/new" | 94 | # new_issue_url: "http://redmine.sample/projects/:issues_tracker_id/issues/new" |
94 | # | 95 | # |
95 | # jira: | 96 | # jira: |
97 | + # title: "Atlassian Jira" | ||
96 | # project_url: "http://jira.sample/issues/?jql=project=:issues_tracker_id" | 98 | # project_url: "http://jira.sample/issues/?jql=project=:issues_tracker_id" |
97 | # issues_url: "http://jira.sample/browse/:id" | 99 | # issues_url: "http://jira.sample/browse/:id" |
98 | # new_issue_url: "http://jira.sample/secure/CreateIssue.jspa" | 100 | # new_issue_url: "http://jira.sample/secure/CreateIssue.jspa" |
@@ -206,6 +208,7 @@ test: | @@ -206,6 +208,7 @@ test: | ||
206 | <<: *base | 208 | <<: *base |
207 | issues_tracker: | 209 | issues_tracker: |
208 | redmine: | 210 | redmine: |
211 | + title: "Redmine" | ||
209 | project_url: "http://redmine/projects/:issues_tracker_id" | 212 | project_url: "http://redmine/projects/:issues_tracker_id" |
210 | issues_url: "http://redmine/:project_id/:issues_tracker_id/:id" | 213 | issues_url: "http://redmine/:project_id/:issues_tracker_id/:id" |
211 | new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new" | 214 | new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new" |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +Devise::Async.backend = :sidekiq |
config/routes.rb
@@ -99,19 +99,21 @@ Gitlab::Application.routes.draw do | @@ -99,19 +99,21 @@ Gitlab::Application.routes.draw do | ||
99 | # | 99 | # |
100 | resource :profile, only: [:show, :update] do | 100 | resource :profile, only: [:show, :update] do |
101 | member do | 101 | member do |
102 | - get :account | ||
103 | get :history | 102 | get :history |
104 | - get :token | ||
105 | get :design | 103 | get :design |
106 | 104 | ||
107 | - put :update_password | ||
108 | put :reset_private_token | 105 | put :reset_private_token |
109 | put :update_username | 106 | put :update_username |
110 | end | 107 | end |
111 | 108 | ||
112 | scope module: :profiles do | 109 | scope module: :profiles do |
110 | + resource :account, only: [:show, :update] | ||
113 | resource :notifications, only: [:show, :update] | 111 | resource :notifications, only: [:show, :update] |
114 | - resource :password, only: [:new, :create] | 112 | + resource :password, only: [:new, :create, :edit, :update] do |
113 | + member do | ||
114 | + put :reset | ||
115 | + end | ||
116 | + end | ||
115 | resources :keys | 117 | resources :keys |
116 | resources :groups, only: [:index] do | 118 | resources :groups, only: [:index] do |
117 | member do | 119 | member do |
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 => 20130926081215) do | 14 | +ActiveRecord::Schema.define(:version => 20131005191208) 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 |
@@ -283,6 +283,7 @@ ActiveRecord::Schema.define(:version => 20130926081215) do | @@ -283,6 +283,7 @@ ActiveRecord::Schema.define(:version => 20130926081215) do | ||
283 | t.integer "notification_level", :default => 1, :null => false | 283 | t.integer "notification_level", :default => 1, :null => false |
284 | t.datetime "password_expires_at" | 284 | t.datetime "password_expires_at" |
285 | t.integer "created_by_id" | 285 | t.integer "created_by_id" |
286 | + t.string "avatar" | ||
286 | end | 287 | end |
287 | 288 | ||
288 | add_index "users", ["admin"], :name => "index_users_on_admin" | 289 | add_index "users", ["admin"], :name => "index_users_on_admin" |
doc/api/projects.md
@@ -244,6 +244,18 @@ Parameters: | @@ -244,6 +244,18 @@ Parameters: | ||
244 | + `public` (optional) | 244 | + `public` (optional) |
245 | 245 | ||
246 | 246 | ||
247 | +## Remove project | ||
248 | + | ||
249 | +Removes project with all resources(issues, merge requests etc) | ||
250 | + | ||
251 | +``` | ||
252 | +DELETE /projects/:id | ||
253 | +``` | ||
254 | + | ||
255 | +Parameters: | ||
256 | + | ||
257 | ++ `id` (required) - The ID of a project | ||
258 | + | ||
247 | 259 | ||
248 | ## Team members | 260 | ## Team members |
249 | 261 |
doc/install/installation.md
@@ -63,7 +63,7 @@ Make sure you have the right version of Python installed. | @@ -63,7 +63,7 @@ Make sure you have the right version of Python installed. | ||
63 | python --version | 63 | python --version |
64 | 64 | ||
65 | # If it's Python 3 you might need to install Python 2 separately | 65 | # If it's Python 3 you might need to install Python 2 separately |
66 | - sudo apt-get install python2.7 | 66 | + sudo apt-get install -y python2.7 |
67 | 67 | ||
68 | # Make sure you can access Python via python2 | 68 | # Make sure you can access Python via python2 |
69 | python2 --version | 69 | python2 --version |
@@ -72,7 +72,7 @@ Make sure you have the right version of Python installed. | @@ -72,7 +72,7 @@ Make sure you have the right version of Python installed. | ||
72 | sudo ln -s /usr/bin/python /usr/bin/python2 | 72 | sudo ln -s /usr/bin/python /usr/bin/python2 |
73 | 73 | ||
74 | # For reStructuredText markup language support install required package: | 74 | # For reStructuredText markup language support install required package: |
75 | - sudo apt-get install python-docutils | 75 | + sudo apt-get install -y python-docutils |
76 | 76 | ||
77 | **Note:** In order to receive mail notifications, make sure to install a | 77 | **Note:** In order to receive mail notifications, make sure to install a |
78 | mail server. By default, Debian is shipped with exim4 whereas Ubuntu | 78 | mail server. By default, Debian is shipped with exim4 whereas Ubuntu |
features/profile/profile.feature
@@ -12,26 +12,31 @@ Feature: Profile | @@ -12,26 +12,31 @@ Feature: Profile | ||
12 | And I should see new contact info | 12 | And I should see new contact info |
13 | 13 | ||
14 | Scenario: I change my password without old one | 14 | Scenario: I change my password without old one |
15 | - Given I visit profile account page | 15 | + Given I visit profile password page |
16 | When I try change my password w/o old one | 16 | When I try change my password w/o old one |
17 | Then I should see a missing password error message | 17 | Then I should see a missing password error message |
18 | - And I should be redirected to account page | 18 | + And I should be redirected to password page |
19 | 19 | ||
20 | Scenario: I change my password | 20 | Scenario: I change my password |
21 | - Given I visit profile account page | 21 | + Given I visit profile password page |
22 | Then I change my password | 22 | Then I change my password |
23 | And I should be redirected to sign in page | 23 | And I should be redirected to sign in page |
24 | 24 | ||
25 | + Scenario: I edit my avatar | ||
26 | + Given I visit profile page | ||
27 | + Then I change my avatar | ||
28 | + And I should see new avatar | ||
29 | + | ||
25 | Scenario: My password is expired | 30 | Scenario: My password is expired |
26 | Given my password is expired | 31 | Given my password is expired |
27 | And I am not an ldap user | 32 | And I am not an ldap user |
28 | - And I visit profile account page | 33 | + Given I visit profile password page |
29 | Then I redirected to expired password page | 34 | Then I redirected to expired password page |
30 | And I submit new password | 35 | And I submit new password |
31 | And I redirected to sign in page | 36 | And I redirected to sign in page |
32 | 37 | ||
33 | Scenario: I unsuccessfully change my password | 38 | Scenario: I unsuccessfully change my password |
34 | - Given I visit profile account page | 39 | + Given I visit profile password page |
35 | When I unsuccessfully change my password | 40 | When I unsuccessfully change my password |
36 | Then I should see a password error message | 41 | Then I should see a password error message |
37 | 42 |
features/steps/profile/profile.rb
@@ -3,9 +3,7 @@ class Profile < Spinach::FeatureSteps | @@ -3,9 +3,7 @@ class Profile < Spinach::FeatureSteps | ||
3 | include SharedPaths | 3 | include SharedPaths |
4 | 4 | ||
5 | step 'I should see my profile info' do | 5 | step 'I should see my profile info' do |
6 | - page.should have_content "Profile" | ||
7 | - page.should have_content @user.name | ||
8 | - page.should have_content @user.email | 6 | + page.should have_content "Profile settings" |
9 | end | 7 | end |
10 | 8 | ||
11 | step 'I change my contact info' do | 9 | step 'I change my contact info' do |
@@ -22,6 +20,17 @@ class Profile < Spinach::FeatureSteps | @@ -22,6 +20,17 @@ class Profile < Spinach::FeatureSteps | ||
22 | @user.twitter.should == 'testtwitter' | 20 | @user.twitter.should == 'testtwitter' |
23 | end | 21 | end |
24 | 22 | ||
23 | + step 'I change my avatar' do | ||
24 | + attach_file(:user_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png')) | ||
25 | + click_button "Save changes" | ||
26 | + @user.reload | ||
27 | + end | ||
28 | + | ||
29 | + step 'I should see new avatar' do | ||
30 | + @user.avatar.should be_instance_of AttachmentUploader | ||
31 | + @user.avatar.url.should == "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png" | ||
32 | + end | ||
33 | + | ||
25 | step 'I try change my password w/o old one' do | 34 | step 'I try change my password w/o old one' do |
26 | within '.update-password' do | 35 | within '.update-password' do |
27 | fill_in "user_password", with: "222333" | 36 | fill_in "user_password", with: "222333" |
@@ -124,8 +133,12 @@ class Profile < Spinach::FeatureSteps | @@ -124,8 +133,12 @@ class Profile < Spinach::FeatureSteps | ||
124 | current_path.should == new_user_session_path | 133 | current_path.should == new_user_session_path |
125 | end | 134 | end |
126 | 135 | ||
136 | + step 'I should be redirected to password page' do | ||
137 | + current_path.should == edit_profile_password_path | ||
138 | + end | ||
139 | + | ||
127 | step 'I should be redirected to account page' do | 140 | step 'I should be redirected to account page' do |
128 | - current_path.should == account_profile_path | 141 | + current_path.should == profile_account_path |
129 | end | 142 | end |
130 | 143 | ||
131 | step 'I click on my profile picture' do | 144 | step 'I click on my profile picture' do |
features/steps/shared/paths.rb
@@ -65,8 +65,12 @@ module SharedPaths | @@ -65,8 +65,12 @@ module SharedPaths | ||
65 | visit profile_path | 65 | visit profile_path |
66 | end | 66 | end |
67 | 67 | ||
68 | + step 'I visit profile password page' do | ||
69 | + visit edit_profile_password_path | ||
70 | + end | ||
71 | + | ||
68 | step 'I visit profile account page' do | 72 | step 'I visit profile account page' do |
69 | - visit account_profile_path | 73 | + visit profile_account_path |
70 | end | 74 | end |
71 | 75 | ||
72 | step 'I visit profile SSH keys page' do | 76 | step 'I visit profile SSH keys page' do |
lib/api/groups.rb
lib/api/projects.rb
@@ -129,6 +129,16 @@ module API | @@ -129,6 +129,16 @@ module API | ||
129 | end | 129 | end |
130 | end | 130 | end |
131 | 131 | ||
132 | + # Remove project | ||
133 | + # | ||
134 | + # Parameters: | ||
135 | + # id (required) - The ID of a project | ||
136 | + # Example Request: | ||
137 | + # DELETE /projects/:id | ||
138 | + delete ":id" do | ||
139 | + authorize! :remove_project, user_project | ||
140 | + user_project.destroy | ||
141 | + end | ||
132 | 142 | ||
133 | # Mark this project as forked from another | 143 | # Mark this project as forked from another |
134 | # | 144 | # |
spec/features/notes_on_merge_requests_spec.rb
@@ -216,12 +216,6 @@ describe "On a merge request diff", js: true, focus: true do | @@ -216,12 +216,6 @@ describe "On a merge request diff", js: true, focus: true do | ||
216 | end | 216 | end |
217 | end | 217 | end |
218 | 218 | ||
219 | - it do | ||
220 | - within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do | ||
221 | - should have_no_css(".js-temp-notes-holder") | ||
222 | - end | ||
223 | - end | ||
224 | - | ||
225 | it 'should be added as discussion' do | 219 | it 'should be added as discussion' do |
226 | should have_content("Another comment on line 17") | 220 | should have_content("Another comment on line 17") |
227 | should have_css(".notes_holder") | 221 | should have_css(".notes_holder") |
spec/features/profile_spec.rb
@@ -12,7 +12,7 @@ describe "Profile account page" do | @@ -12,7 +12,7 @@ describe "Profile account page" do | ||
12 | describe "when signup is enabled" do | 12 | describe "when signup is enabled" do |
13 | before do | 13 | before do |
14 | Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) | 14 | Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) |
15 | - visit account_profile_path | 15 | + visit profile_account_path |
16 | end | 16 | end |
17 | 17 | ||
18 | it { page.should have_content("Remove account") } | 18 | it { page.should have_content("Remove account") } |
@@ -26,12 +26,12 @@ describe "Profile account page" do | @@ -26,12 +26,12 @@ describe "Profile account page" do | ||
26 | describe "when signup is disabled" do | 26 | describe "when signup is disabled" do |
27 | before do | 27 | before do |
28 | Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) | 28 | Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) |
29 | - visit account_profile_path | 29 | + visit profile_account_path |
30 | end | 30 | end |
31 | 31 | ||
32 | it "should not have option to remove account" do | 32 | it "should not have option to remove account" do |
33 | page.should_not have_content("Remove account") | 33 | page.should_not have_content("Remove account") |
34 | - current_path.should == account_profile_path | 34 | + current_path.should == profile_account_path |
35 | end | 35 | end |
36 | end | 36 | end |
37 | end | 37 | end |
spec/features/security/profile_access_spec.rb
@@ -29,7 +29,7 @@ describe "Users Security" do | @@ -29,7 +29,7 @@ describe "Users Security" do | ||
29 | end | 29 | end |
30 | 30 | ||
31 | describe "GET /profile/account" do | 31 | describe "GET /profile/account" do |
32 | - subject { account_profile_path } | 32 | + subject { profile_account_path } |
33 | 33 | ||
34 | it { should be_allowed_for @u1 } | 34 | it { should be_allowed_for @u1 } |
35 | it { should be_allowed_for :admin } | 35 | it { should be_allowed_for :admin } |
spec/helpers/application_helper_spec.rb
@@ -38,6 +38,24 @@ describe ApplicationHelper do | @@ -38,6 +38,24 @@ describe ApplicationHelper do | ||
38 | current_action?(:baz, :bar, :foo).should be_true | 38 | current_action?(:baz, :bar, :foo).should be_true |
39 | end | 39 | end |
40 | end | 40 | end |
41 | + | ||
42 | + describe "avatar_icon" do | ||
43 | + avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png') | ||
44 | + | ||
45 | + it "should return an url for the avatar" do | ||
46 | + user = create(:user) | ||
47 | + user.avatar = File.open(avatar_file_path) | ||
48 | + user.save! | ||
49 | + avatar_icon(user.email).to_s.should == "/uploads/user/avatar/#{ user.id }/gitlab_logo.png" | ||
50 | + end | ||
51 | + | ||
52 | + it "should call gravatar_icon when no avatar is present" do | ||
53 | + user = create(:user) | ||
54 | + user.save! | ||
55 | + stub!(:gravatar_icon).and_return('gravatar_method_called') | ||
56 | + avatar_icon(user.email).to_s.should == "gravatar_method_called" | ||
57 | + end | ||
58 | + end | ||
41 | 59 | ||
42 | describe "gravatar_icon" do | 60 | describe "gravatar_icon" do |
43 | let(:user_email) { 'user@email.com' } | 61 | let(:user_email) { 'user@email.com' } |
@@ -0,0 +1,11 @@ | @@ -0,0 +1,11 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe ProjectsHelper do | ||
4 | + describe '#project_issues_trackers' do | ||
5 | + it "returns the correct issues trackers available" do | ||
6 | + project_issues_trackers.should == | ||
7 | + "<option value=\"redmine\">Redmine</option>\n" \ | ||
8 | + "<option value=\"gitlab\">GitLab</option>" | ||
9 | + end | ||
10 | + end | ||
11 | +end |
spec/requests/api/projects_spec.rb
@@ -730,4 +730,42 @@ describe API::API do | @@ -730,4 +730,42 @@ describe API::API do | ||
730 | end | 730 | end |
731 | end | 731 | end |
732 | end | 732 | end |
733 | + | ||
734 | + describe "DELETE /projects/:id" do | ||
735 | + context "when authenticated as user" do | ||
736 | + it "should remove project" do | ||
737 | + delete api("/projects/#{project.id}", user) | ||
738 | + response.status.should == 200 | ||
739 | + end | ||
740 | + | ||
741 | + it "should not remove a project if not an owner" do | ||
742 | + user3 = create(:user) | ||
743 | + project.team << [user3, :developer] | ||
744 | + delete api("/projects/#{project.id}", user3) | ||
745 | + response.status.should == 403 | ||
746 | + end | ||
747 | + | ||
748 | + it "should not remove a non existing project" do | ||
749 | + delete api("/projects/1328", user) | ||
750 | + response.status.should == 404 | ||
751 | + end | ||
752 | + | ||
753 | + it "should not remove a project not attached to user" do | ||
754 | + delete api("/projects/#{project.id}", user2) | ||
755 | + response.status.should == 404 | ||
756 | + end | ||
757 | + end | ||
758 | + | ||
759 | + context "when authenticated as admin" do | ||
760 | + it "should remove any existing project" do | ||
761 | + delete api("/projects/#{project.id}", admin) | ||
762 | + response.status.should == 200 | ||
763 | + end | ||
764 | + | ||
765 | + it "should not remove a non existing project" do | ||
766 | + delete api("/projects/1328", admin) | ||
767 | + response.status.should == 404 | ||
768 | + end | ||
769 | + end | ||
770 | + end | ||
733 | end | 771 | end |
spec/routing/routing_spec.rb
@@ -128,7 +128,7 @@ end | @@ -128,7 +128,7 @@ end | ||
128 | # profile_update PUT /profile/update(.:format) profile#update | 128 | # profile_update PUT /profile/update(.:format) profile#update |
129 | describe ProfilesController, "routing" do | 129 | describe ProfilesController, "routing" do |
130 | it "to #account" do | 130 | it "to #account" do |
131 | - get("/profile/account").should route_to('profiles#account') | 131 | + get("/profile/account").should route_to('profiles/accounts#show') |
132 | end | 132 | end |
133 | 133 | ||
134 | it "to #history" do | 134 | it "to #history" do |