diff options
103 files changed, 1040 insertions, 387 deletions
diff --git a/CHANGELOG b/CHANGELOG index c4e411e2df5..1843311d763 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -13,6 +13,8 @@ v 6.2.0 - Rake tasks for web hooks management (Jonhnny Weslley) - Extended User API to expose admin and can_create_group for user creation/updating (Boyan Tabakov) - API: Remove group + - Avatar upload on profile page with a maximum of 200KB (Steven Thonus) + - Store the sessions in Redis instead of the cookie store v 6.1.0 - Project specific IDs for issues, mr, milestones @@ -16,6 +16,7 @@ gem "pg", group: :postgres # Auth gem "devise", '~> 2.2' +gem "devise-async" gem 'omniauth', "~> 1.1.3" gem 'omniauth-google-oauth2' gem 'omniauth-twitter' @@ -23,7 +24,7 @@ gem 'omniauth-github' # Extracting information from a git repository # Provide access to Gitlab::Git library -gem "gitlab_git", "~> 3.0.0.beta1" +gem "gitlab_git", "~> 3.0.0.rc1" # Ruby/Rack Git Smart-HTTP Server Handler gem 'gitlab-grack', '~> 1.0.1', require: 'grack' diff --git a/Gemfile.lock b/Gemfile.lock index ce3aeb7994b..38d53eca14e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -106,6 +106,8 @@ GEM orm_adapter (~> 0.1) railties (~> 3.1) warden (~> 1.2.1) + devise-async (0.8.0) + devise (>= 2.2, < 3.2) diff-lcs (1.2.4) dotenv (0.8.0) email_spec (1.4.0) @@ -177,7 +179,7 @@ GEM gitlab-pygments.rb (0.3.2) posix-spawn (~> 0.3.6) yajl-ruby (~> 1.1.0) - gitlab_git (3.0.0.beta1) + gitlab_git (3.0.0.rc1) activesupport (~> 3.2.13) github-linguist (~> 2.3.4) gitlab-grit (~> 2.6.0) @@ -564,6 +566,7 @@ DEPENDENCIES d3_rails (~> 3.1.4) database_cleaner devise (~> 2.2) + devise-async email_spec enumerize factory_girl_rails @@ -578,7 +581,7 @@ DEPENDENCIES gitlab-gollum-lib (~> 1.0.1) gitlab-grack (~> 1.0.1) gitlab-pygments.rb (~> 0.3.2) - gitlab_git (~> 3.0.0.beta1) + gitlab_git (~> 3.0.0.rc1) gitlab_meta (= 6.0) gitlab_omniauth-ldap (= 1.0.3) gon diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee index e7974611cbe..744f3086d55 100644 --- a/app/assets/javascripts/profile.js.coffee +++ b/app/assets/javascripts/profile.js.coffee @@ -16,3 +16,13 @@ $ -> $('.update-notifications').on 'ajax:complete', -> $(this).find('.btn-save').enableButton() + + + $('.js-choose-user-avatar-button').bind "click", -> + form = $(this).closest("form") + form.find(".js-user-avatar-input").click() + + $('.js-user-avatar-input').bind "change", -> + form = $(this).closest("form") + filename = $(this).val().replace(/^.*[\\\/]/, '') + form.find(".js-avatar-filename").text(filename) diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee index 8286ca2f0c1..327667fede0 100644 --- a/app/assets/javascripts/users_select.js.coffee +++ b/app/assets/javascripts/users_select.js.coffee @@ -1,9 +1,11 @@ $ -> userFormatResult = (user) -> - avatar = gon.gravatar_url - avatar = avatar.replace('%{hash}', md5(user.email)) - avatar = avatar.replace('%{size}', '24') - + if user.avatar + avatar = user.avatar.url + else + avatar = gon.gravatar_url + avatar = avatar.replace('%{hash}', md5(user.email)) + avatar = avatar.replace('%{size}', '24') markup = "<div class='user-result'>" markup += "<div class='user-image'><img class='avatar s24' src='" + avatar + "'></div>" markup += "<div class='user-name'>" + user.name + "</div>" diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index 1572227ec3a..a3156ec10fc 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -270,27 +270,6 @@ li.note { } } -.oauth_select_holder { - padding: 20px; - img { - padding: 5px; - margin-right: 10px; - } - .active { - img { - border: 1px solid #ccc; - background: $hover; - @include border-radius(5px); - } - } -} - -.btn-build-token { - float: left; - padding: 6px 20px; - margin-right: 12px; -} - .gitlab-promo { a { color: #aaa; diff --git a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss b/app/assets/stylesheets/gitlab_bootstrap/avatar.scss index c23970c13eb..98fbbd64e99 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/avatar.scss @@ -19,4 +19,5 @@ &.s32 { width: 32px; height: 32px; margin-right: 10px; } &.s60 { width: 60px; height: 60px; margin-right: 12px; } &.s90 { width: 90px; height: 90px; margin-right: 15px; } + &.s160 { width: 160px; height: 160px; margin-right: 20px; } } diff --git a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss index fcf1159cd33..f0a77bb8add 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/blocks.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/blocks.scss @@ -12,7 +12,7 @@ .ui-box { background: #FFF; margin-bottom: 20px; - border: 1px solid #CCC; + border: 1px solid #DDD; word-wrap: break-word; &.small-box { diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss index be6fb29c817..45a83cb6081 100644 --- a/app/assets/stylesheets/sections/commits.scss +++ b/app/assets/stylesheets/sections/commits.scss @@ -419,7 +419,6 @@ .commit-title { margin: 0; font-size: 20px; - font-weight: bold; } .commit-description { @@ -505,4 +504,4 @@ li.commit { @extend .cgray; } } -}
\ No newline at end of file +} diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss index 3f7825d71ce..04df7656b0d 100644 --- a/app/assets/stylesheets/sections/dashboard.scss +++ b/app/assets/stylesheets/sections/dashboard.scss @@ -60,7 +60,7 @@ } a { - border-color: #CCC !important; + border-color: #DDD !important; } } } diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss index 5c7516ce6f9..21f4ed0577d 100644 --- a/app/assets/stylesheets/sections/profile.scss +++ b/app/assets/stylesheets/sections/profile.scss @@ -4,3 +4,41 @@ margin-bottom: 0; } } + +.account-page { + fieldset { + margin-bottom: 15px; + border-bottom: 1px dashed #ddd; + padding-bottom: 15px; + + &:last-child { + border: none; + } + + legend { + border: none; + margin: 0; + } + } +} + +.oauth_select_holder { + img { + padding: 2px; + margin-right: 10px; + } + .active { + img { + border: 1px solid #4BD; + background: $hover; + @include border-radius(5px); + } + } +} + +.btn-build-token { + float: left; + padding: 6px 20px; + margin-right: 12px; +} + diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d974600dcc1..85b95862a17 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -63,6 +63,15 @@ class ApplicationController < ActionController::Base def project id = params[:project_id] || params[:id] + # Redirect from + # localhost/group/project.git + # to + # localhost/group/project + # + if id =~ /\.git\Z/ + redirect_to request.original_url.gsub(/\.git\Z/, '') and return + end + @project = Project.find_with_namespace(id) if @project and can?(current_user, :read_project, @project) diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb new file mode 100644 index 00000000000..fe121691a10 --- /dev/null +++ b/app/controllers/profiles/accounts_controller.rb @@ -0,0 +1,7 @@ +class Profiles::AccountsController < ApplicationController + layout "profile" + + def show + @user = current_user + end +end diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb index 432899f857d..df6954554ea 100644 --- a/app/controllers/profiles/passwords_controller.rb +++ b/app/controllers/profiles/passwords_controller.rb @@ -1,10 +1,11 @@ class Profiles::PasswordsController < ApplicationController - layout 'navless' + layout :determine_layout - skip_before_filter :check_password_expiration + skip_before_filter :check_password_expiration, only: [:new, :create] before_filter :set_user before_filter :set_title + before_filter :authorize_change_password! def new end @@ -26,6 +27,32 @@ class Profiles::PasswordsController < ApplicationController end end + def edit + end + + def update + password_attributes = params[:user].select do |key, value| + %w(password password_confirmation).include?(key.to_s) + end + + unless @user.valid_password?(params[:user][:current_password]) + redirect_to edit_profile_password_path, alert: 'You must provide a valid current password' + return + end + + if @user.update_attributes(password_attributes) + flash[:notice] = "Password was successfully updated. Please login with it" + redirect_to new_user_session_path + else + render 'edit' + end + end + + def reset + current_user.send_reset_password_instructions + redirect_to edit_profile_password_path, notice: 'We sent you an email with reset password instructions' + end + private def set_user @@ -35,4 +62,16 @@ class Profiles::PasswordsController < ApplicationController def set_title @title = "New password" end + + def determine_layout + if [:new, :create].include?(action_name.to_sym) + 'navless' + else + 'profile' + end + end + + def authorize_change_password! + return render_404 if @user.ldap_user? + end end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 75f12f8a6af..47cfc5e63f5 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -2,7 +2,6 @@ class ProfilesController < ApplicationController include ActionView::Helpers::SanitizeHelper before_filter :user - before_filter :authorize_change_password!, only: :update_password before_filter :authorize_change_username!, only: :update_username layout 'profile' @@ -13,9 +12,6 @@ class ProfilesController < ApplicationController def design end - def account - end - def update if @user.update_attributes(params[:user]) flash[:notice] = "Profile was successfully updated" @@ -29,33 +25,12 @@ class ProfilesController < ApplicationController end end - def token - end - - def update_password - password_attributes = params[:user].select do |key, value| - %w(password password_confirmation).include?(key.to_s) - end - - unless @user.valid_password?(params[:user][:current_password]) - redirect_to account_profile_path, alert: 'You must provide a valid current password' - return - end - - if @user.update_attributes(password_attributes) - flash[:notice] = "Password was successfully updated. Please login with it" - redirect_to new_user_session_path - else - render 'account' - end - end - def reset_private_token if current_user.reset_authentication_token! flash[:notice] = "Token was successfully updated" end - redirect_to account_profile_path + redirect_to profile_account_path end def history @@ -76,10 +51,6 @@ class ProfilesController < ApplicationController @user = current_user end - def authorize_change_password! - return render_404 if @user.ldap_user? - end - def authorize_change_username! return render_404 unless @user.can_change_username? end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 7e5c10fee05..0e48889ebf8 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -49,6 +49,15 @@ module ApplicationHelper args.any? { |v| v.to_s.downcase == action_name } end + def avatar_icon(user_email = '', size = nil) + user = User.find_by_email(user_email) + if user && user.avatar.present? + user.avatar.url + else + gravatar_icon(user_email, size) + end + end + def gravatar_icon(user_email = '', size = nil) size = 40 if size.nil? || size <= 0 @@ -96,7 +105,7 @@ module ApplicationHelper groups = current_user.authorized_groups.map { |group| { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) } } default_nav = [ - { label: "My Profile", url: profile_path }, + { label: "My Profile settings", url: profile_path }, { label: "My SSH Keys", url: profile_keys_path }, { label: "My Dashboard", url: root_path }, { label: "Admin Section", url: admin_root_path }, diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index f8f84ff8b62..7e24cbd3e58 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -108,7 +108,7 @@ module CommitsHelper source_name = commit.send "#{options[:source]}_name".to_sym source_email = commit.send "#{options[:source]}_email".to_sym text = if options[:avatar] - avatar = image_tag(gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") + avatar = image_tag(avatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "") %Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>} else source_name diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb index 375f8861dae..0210bea2f40 100644 --- a/app/helpers/gitlab_markdown_helper.rb +++ b/app/helpers/gitlab_markdown_helper.rb @@ -34,7 +34,8 @@ module GitlabMarkdownHelper # see https://github.com/vmg/redcarpet#darling-i-packed-you-a-couple-renderers-for-lunch- filter_html: true, with_toc_data: true, - hard_wrap: true) + hard_wrap: true, + safe_links_only: true) @markdown = Redcarpet::Markdown.new(gitlab_renderer, # see https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use no_intra_emphasis: true, @@ -57,4 +58,97 @@ module GitlabMarkdownHelper wiki_page.formatted_content.html_safe end end + + # text - whole text from a markdown file + # project_path_with_namespace - namespace/projectname, eg. gitlabhq/gitlabhq + # ref - name of the branch or reference, eg. stable + # requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from + # wiki - whether the markdown is from wiki or not + def create_relative_links(text, project_path_with_namespace, ref, requested_path, wiki = false) + paths = extract_paths(text) + paths.each do |file_path| + new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref) + # Replacing old string with a new one with brackets ]() to prevent replacing occurence of a word + # e.g. If we have a markdown like [test](test) this will replace ](test) and not the word test + text.gsub!("](#{file_path})", "](/#{new_path})") + end + text + end + + def extract_paths(markdown_text) + all_markdown_paths = pick_out_paths(markdown_text) + paths = remove_empty(all_markdown_paths) + select_relative(paths) + end + + # Split the markdown text to each line and find all paths, this will match anything with - ]("some_text") + def pick_out_paths(markdown_text) + markdown_text.split("\n").map { |text| text.scan(/\]\(([^(]+)\)/) } + end + + # Removes any empty result produced by not matching the regexp + def remove_empty(paths) + paths.reject{|l| l.empty? }.flatten + end + + # Reject any path that contains ignored protocol + # eg. reject "https://gitlab.org} but accept "doc/api/README.md" + def select_relative(paths) + paths.reject{|path| ignored_protocols.map{|protocol| path.include?(protocol)}.any?} + end + + def ignored_protocols + ["http://","https://", "ftp://", "mailto:"] + end + + def rebuild_path(path_with_namespace, path, requested_path, ref) + file_path = relative_file_path(path, requested_path) + [ + path_with_namespace, + path_with_ref(file_path, ref), + file_path + ].compact.join("/") + end + + # Checks if the path exists in the repo + # eg. checks if doc/README.md exists, if it doesn't then it is a wiki link + def path_with_ref(path, ref) + if file_exists?(path) + "#{local_path(path)}/#{correct_ref(ref)}" + else + "wikis" + end + end + + def relative_file_path(path, requested_path) + nested_path = build_nested_path(path, requested_path) + return nested_path if file_exists?(nested_path) + path + end + + # Covering a special case, when the link is referencing file in the same directory eg: + # If we are at doc/api/README.md and the README.md contains relative links like [Users](users.md) + # this takes the request path(doc/api/README.md), and replaces the README.md with users.md so the path looks like doc/api/users.md + def build_nested_path(path, request_path) + return path unless request_path + base = request_path.split("/") + base.pop + (base + [path]).join("/") + end + + def file_exists?(path) + return false if path.nil? || path.empty? + File.exists?(Rails.root.join(path)) + end + + # Check if the path is pointing to a directory(tree) or a file(blob) + # eg. doc/api is directory and doc/README.md is file + def local_path(path) + File.directory?(Rails.root.join(path)) ? "tree" : "blob" + end + + # We will assume that if no ref exists we can point to master + def correct_ref(ref) + ref ? ref : "master" + end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 9071c688df1..d4f1a8f741f 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -5,10 +5,10 @@ module ProjectsHelper def link_to_project project link_to project do - title = content_tag(:strong, project.name) + title = content_tag(:span, project.name, class: 'projet-name') if project.namespace - namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'tiny') + namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'namespace-name') title = namespace + title end @@ -25,7 +25,7 @@ module ProjectsHelper author_html = "" # Build avatar image tag - 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] + 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] # Build name span tag author_html << content_tag(:span, sanitize(author.name), class: 'author') if opts[:name] @@ -80,6 +80,18 @@ module ProjectsHelper @project.milestones.active.order("id desc").all end + def project_issues_trackers + values = Project.issues_tracker.values.map do |tracker_key| + if tracker_key.to_sym == :gitlab + ['GitLab', tracker_key] + else + [Gitlab.config.issues_tracker[tracker_key]['title'] || tracker_key, tracker_key] + end + end + + options_for_select(values) + end + private def get_project_nav_tabs(project, current_user) @@ -119,4 +131,13 @@ module ProjectsHelper "your@email.com" end end + + def repository_size + "#{@project.repository.size} MB" + rescue + # In order to prevent 500 error + # when application cannot allocate memory + # to calculate repo size - just show 'Unknown' + 'unknown' + end end diff --git a/app/models/gollum_wiki.rb b/app/models/gollum_wiki.rb index d1edbab4533..05fc2a745b4 100644 --- a/app/models/gollum_wiki.rb +++ b/app/models/gollum_wiki.rb @@ -45,6 +45,10 @@ class GollumWiki end end + def empty? + pages.empty? + end + # Returns an Array of Gitlab WikiPage instances or an # empty Array if this Wiki has no pages. def pages diff --git a/app/models/tree.rb b/app/models/tree.rb index 5fbad19b468..ed06cb1a128 100644 --- a/app/models/tree.rb +++ b/app/models/tree.rb @@ -7,7 +7,8 @@ class Tree @entries = Gitlab::Git::Tree.where(git_repo, sha, path) if readme_tree = @entries.find(&:readme?) - @readme = Gitlab::Git::Blob.find(git_repo, sha, readme_tree.name) + readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name) + @readme = Gitlab::Git::Blob.find(git_repo, sha, readme_path) end end diff --git a/app/models/user.rb b/app/models/user.rb index c61b074f504..29c53b88331 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -38,13 +38,16 @@ # created_by_id :integer # +require 'carrierwave/orm/activerecord' +require 'file_size_validator' + class User < ActiveRecord::Base - devise :database_authenticatable, :token_authenticatable, :lockable, + devise :database_authenticatable, :token_authenticatable, :lockable, :async, :recoverable, :rememberable, :trackable, :validatable, :omniauthable, :registerable attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username, :skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password, - :extern_uid, :provider, :password_expires_at, + :extern_uid, :provider, :password_expires_at, :avatar, as: [:default, :admin] attr_accessible :projects_limit, :can_create_group, @@ -113,6 +116,8 @@ class User < ActiveRecord::Base validate :namespace_uniq, if: ->(user) { user.username_changed? } + validates :avatar, file_size: { maximum: 100.kilobytes.to_i } + before_validation :generate_password, on: :create before_validation :sanitize_attrs @@ -150,6 +155,8 @@ class User < ActiveRecord::Base end end + mount_uploader :avatar, AttachmentUploader + # Scopes scope :admins, -> { where(admin: true) } scope :blocked, -> { with_state(:blocked) } @@ -391,4 +398,4 @@ class User < ActiveRecord::Base self end -end +end
\ No newline at end of file diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb index acbb719b69a..b82e7d9a03f 100644 --- a/app/observers/project_observer.rb +++ b/app/observers/project_observer.rb @@ -14,6 +14,11 @@ class ProjectObserver < BaseObserver log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"") end + + if project.wiki_enabled? + # force the creation of a wiki, + GollumWiki.new(project, project.owner).wiki + end end def after_update(project) diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index f9d43e60de6..3ad41de397f 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -112,6 +112,7 @@ class GitPushService # ref: String, # user_id: String, # user_name: String, + # project_id: String, # repository: { # name: String, # url: String, @@ -136,6 +137,7 @@ class GitPushService ref: ref, user_id: user.id, user_name: user.name, + project_id: project.id, repository: { name: project.name, url: project.url_to_repo, diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml index 9ed788d0d9d..3064763b993 100644 --- a/app/views/admin/dashboard/index.html.haml +++ b/app/views/admin/dashboard/index.html.haml @@ -1,30 +1,35 @@ +%h3.page-title + Admin area +%p.light + You can manage projects, users and other GitLab data from here. +%hr .admin_dash.row .span4 - .ui-box - .title Projects - .data.padded + .light-well + %h4 Projects + .data = link_to admin_projects_path do %h1= Project.count %hr - = link_to 'New Project', new_project_path, class: "btn btn-small" + = link_to 'New Project', new_project_path, class: "btn btn-new" .span4 - .ui-box - .title Users - .data.padded + .light-well + %h4 Users + .data = link_to admin_users_path do %h1= User.count %hr - = link_to 'New User', new_admin_user_path, class: "btn btn-small" + = link_to 'New User', new_admin_user_path, class: "btn btn-new" .span4 - .ui-box - .title Groups - .data.padded + .light-well + %h4 Groups + .data = link_to admin_groups_path do %h1= Group.count %hr - = link_to 'New Group', new_admin_group_path, class: "btn btn-small" + = link_to 'New Group', new_admin_group_path, class: "btn btn-new" -.row +.row.prepend-top-10 .span4 %h4 Latest projects %hr diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 3df9903e409..df9fc687478 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -20,7 +20,7 @@ .title Account: .pull-right - = image_tag gravatar_icon(@user.email, 32), class: "avatar s32" + = image_tag avatar_icon(@user.email, 32), class: "avatar s32" %ul.well-list %li %span.light Name: @@ -117,11 +117,7 @@ - tm = project.team.find_tm(@user.id) %li.users_project = link_to admin_project_path(project), class: dom_class(project) do - - if project.namespace - = project.namespace.human_name - \/ - %strong.well-title - = truncate(project.name, length: 45) + = project.name_with_namespace - if tm .pull-right diff --git a/app/views/dashboard/issues.atom.builder b/app/views/dashboard/issues.atom.builder index 0f0f3466e92..f5413557783 100644 --- a/app/views/dashboard/issues.atom.builder +++ b/app/views/dashboard/issues.atom.builder @@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link :href => project_issue_url(issue.project, issue) xml.title truncate(issue.title, :length => 80) xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email) + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email) xml.author do |author| xml.name issue.author_name xml.email issue.author_email diff --git a/app/views/dashboard/show.atom.builder b/app/views/dashboard/show.atom.builder index a913df92299..f4cf24ccd99 100644 --- a/app/views/dashboard/show.atom.builder +++ b/app/views/dashboard/show.atom.builder @@ -17,7 +17,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link :href => event_link xml.title truncate(event_title, :length => 80) xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email) + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email) xml.author do |author| xml.name event.author_name xml.email event.author_email diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index b3543460d65..892dacafa62 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -4,7 +4,7 @@ #{time_ago_in_words(event.created_at)} ago. = cache event do - = image_tag gravatar_icon(event.author_email), class: "avatar s24", alt:'' + = image_tag avatar_icon(event.author_email), class: "avatar s24", alt:'' - if event.push? = render "events/event/push", event: event diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index adba9a5f619..f181df23eb4 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -7,7 +7,7 @@ = link_to project_commits_path(event.project, event.ref_name) do %strong= truncate(event.ref_name, length: 30) at - %strong= link_to_project event.project + = link_to_project event.project - if event.push_with_commits? - project = event.project diff --git a/app/views/groups/issues.atom.builder b/app/views/groups/issues.atom.builder index 701747bdbc3..f2005193f83 100644 --- a/app/views/groups/issues.atom.builder +++ b/app/views/groups/issues.atom.builder @@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link :href => project_issue_url(issue.project, issue) xml.title truncate(issue.title, :length => 80) xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email) + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email) xml.author do |author| xml.name issue.author_name xml.email issue.author_email diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder index edf03642d82..e07bb7d2fb7 100644 --- a/app/views/groups/show.atom.builder +++ b/app/views/groups/show.atom.builder @@ -16,7 +16,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link :href => event_link xml.title truncate(event_title, :length => 80) xml.updated event.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(event.author_email) + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(event.author_email) xml.author do |author| xml.name event.author_name xml.email event.author_email diff --git a/app/views/help/permissions.html.haml b/app/views/help/permissions.html.haml index bab1e7c0a41..df35f41fc90 100644 --- a/app/views/help/permissions.html.haml +++ b/app/views/help/permissions.html.haml @@ -1,6 +1,8 @@ = render layout: 'help/layout' do %h3.page-title Permissions - %p.light User has different abilities depends on access level he has in particular group or project + %p.light Users have different abilities depending on the access level they have in particular group or project. + %p.light If a user is both in a project group and in the project itself the highest permission level is used. + %p.light If a user is a GitLab administrator they receive all permissions. %hr %h4 Project: diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index 6492c122ba0..80c42fe6387 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -30,11 +30,11 @@ = link_to new_project_path, title: "New project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project' do %i.icon-plus %li - = link_to profile_path, title: "My profile", class: 'has_bottom_tooltip', 'data-original-title' => 'My profile' do + = link_to profile_path, title: "Profile settings", class: 'has_bottom_tooltip', 'data-original-title' => 'Profile settings"' do %i.icon-user %li = link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do %i.icon-signout %li = link_to current_user, class: "profile-pic", id: 'profile-pic' do - = image_tag gravatar_icon(current_user.email, 26), alt: '' + = image_tag avatar_icon(current_user.email, 26), alt: 'User activity' diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 7c3acfc398a..d44cb975ea5 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -2,8 +2,11 @@ = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = link_to profile_path, title: "Profile" do %i.icon-home - = nav_link(path: 'profiles#account') do - = link_to "Account", account_profile_path + = nav_link(controller: :accounts) do + = link_to "Account", profile_account_path + - unless current_user.ldap_user? + = nav_link(controller: :passwords) do + = link_to "Password", edit_profile_password_path = nav_link(controller: :notifications) do = link_to "Notifications", profile_notifications_path = nav_link(controller: :keys) do diff --git a/app/views/profiles/account.html.haml b/app/views/profiles/account.html.haml deleted file mode 100644 index 42c7ec051cb..00000000000 --- a/app/views/profiles/account.html.haml +++ /dev/null @@ -1,141 +0,0 @@ -%h3.page-title - Account settings -%p.light - You can change your password, username and private token here. - - if current_user.ldap_user? - Some options are unavailable for LDAP accounts -%hr - - -.row - .span2 - %ul.nav.nav-pills.nav-stacked.nav-stacked-menu - %li.active - = link_to '#tab-token', 'data-toggle' => 'tab' do - Private Token - %li - = link_to '#tab-password', 'data-toggle' => 'tab' do - Password - - - if show_profile_social_tab? - %li - = link_to '#tab-social', 'data-toggle' => 'tab' do - Social Accounts - - - if show_profile_username_tab? - %li - = link_to '#tab-username', 'data-toggle' => 'tab' do - Change Username - - - if show_profile_remove_tab? - %li - = link_to '#tab-remove', 'data-toggle' => 'tab' do - Remove Account - .span10 - .tab-content - .tab-pane.active#tab-token - %fieldset.update-token - %legend - Private token - %span.cred.pull-right - keep it secret! - %div - = form_for @user, url: reset_private_token_profile_path, method: :put do |f| - .data - %p.slead - Your private token is used to access application resources without authentication. - %br - It can be used for atom feeds or the API. - %p.cgray - - if current_user.private_token - = text_field_tag "token", current_user.private_token, class: "input-xxlarge large_text input-xpadding" - = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token" - - else - %span You don`t have one yet. Click generate to fix it. - = f.submit 'Generate', class: "btn success btn-build-token" - - .tab-pane#tab-password - %fieldset.update-password - %legend Password - - if current_user.ldap_user? - %h3.nothing_here_message Not available for LDAP user - - else - = form_for @user, url: update_password_profile_path, method: :put do |f| - %div - %p.slead - You must provide current password in order to change it. - %br - After a successful password update you will be redirected to login page where you should login with your new password - -if @user.errors.any? - .alert.alert-error - %ul - - @user.errors.full_messages.each do |msg| - %li= msg - .control-group - = f.label :current_password, class: 'cgreen' - .controls= f.password_field :current_password, required: true - .control-group - = f.label :password, 'New password' - .controls= f.password_field :password, required: true - .control-group - = f.label :password_confirmation - .controls - = f.password_field :password_confirmation, required: true - .control-group - .controls - = f.submit 'Save password', class: "btn btn-save" - - - if show_profile_social_tab? - .tab-pane#tab-social - %fieldset - %legend Social Accounts - .oauth_select_holder - %p.hint Tip: Click on icon to activate signin with one of the following services - - enabled_social_providers.each do |provider| - %span{class: oauth_active_class(provider) } - = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) - - - if show_profile_username_tab? - .tab-pane#tab-username - %fieldset.update-username - %legend - Username - %small.cred.pull-right - Changing your username can have unintended side effects! - = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| - %div - .control-group - = f.label :username - .controls - = f.text_field :username, required: true - - %span.loading-gif.hide= image_tag "ajax_loader.gif" - %span.update-success.cgreen.hide - %i.icon-ok - Saved - %span.update-failed.cred.hide - %i.icon-remove - Failed - %ul.cred - %li This will change the web URL for personal projects. - %li This will change the git path to repositories for personal projects. - .controls - = f.submit 'Save username', class: "btn btn-save" - - - if show_profile_remove_tab? - .tab-pane#tab-remove - %fieldset.remove-account - %legend - Remove account - %div - %p Deleting an account has the following effects: - %ul - %li All user content like authored issues, snippets, comments will be removed - - rp = current_user.personal_projects.count - - unless rp.zero? - %li #{pluralize rp, 'personal project'} will be removed and cannot be restored - - if current_user.solo_owned_groups.present? - %li - Next groups will be abandoned. You should transfer or remove them: - %strong #{current_user.solo_owned_groups.map(&:name).join(', ')} - = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove" diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml new file mode 100644 index 00000000000..453ef8b60db --- /dev/null +++ b/app/views/profiles/accounts/show.html.haml @@ -0,0 +1,73 @@ +%h3.page-title + Account settings +%p.light + You can change your username and private token here. + - if current_user.ldap_user? + Some options are unavailable for LDAP accounts +%hr + + +.account-page + %fieldset.update-token + %legend + Private token + %div + = form_for @user, url: reset_private_token_profile_path, method: :put do |f| + .data + %p + Your private token is used to access application resources without authentication. + %br + It can be used for atom feeds or the API. + %span.cred + Keep it secret! + + %p.cgray + - if current_user.private_token + = text_field_tag "token", current_user.private_token, class: "input-xlarge input-xpadding pull-left" + = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token prepend-left-10" + - else + %span You don`t have one yet. Click generate to fix it. + = f.submit 'Generate', class: "btn success btn-build-token" + + + - if show_profile_social_tab? + %fieldset + %legend Social Accounts + .oauth_select_holder.append-bottom-10 + %p Click on icon to activate signin with one of the following services + - enabled_social_providers.each do |provider| + %span{class: oauth_active_class(provider) } + = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) + + - if show_profile_username_tab? + %fieldset.update-username + %legend + Username + = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| + %p + Changing your username will change path to all personl projects! + %div + = f.text_field :username, required: true, class: 'input-xlarge input-xpadding' + + %span.loading-gif.hide= image_tag "ajax_loader.gif" + %p.light + = user_url(@user) + %div + = f.submit 'Save username', class: "btn btn-save" + + - if show_profile_remove_tab? + %fieldset.remove-account + %legend + Remove account + %div + %p Deleting an account has the following effects: + %ul + %li All user content like authored issues, snippets, comments will be removed + - rp = current_user.personal_projects.count + - unless rp.zero? + %li #{pluralize rp, 'personal project'} will be removed and cannot be restored + - if current_user.solo_owned_groups.present? + %li + Next groups will be abandoned. You should transfer or remove them: + %strong #{current_user.solo_owned_groups.map(&:name).join(', ')} + = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove" diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml new file mode 100644 index 00000000000..a5fa6e7f186 --- /dev/null +++ b/app/views/profiles/passwords/edit.html.haml @@ -0,0 +1,32 @@ +%h3.page-title Password +%p.light + Change your password or recover your current one. +%hr +.update-password + = form_for @user, url: profile_password_path, method: :put do |f| + %div + %p.slead + You must provide current password in order to change it. + %br + After a successful password update you will be redirected to login page where you should login with your new password + -if @user.errors.any? + .alert.alert-error + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + .control-group + = f.label :current_password + .controls + = f.password_field :current_password, required: true + %div + = link_to "Forgot your password?", reset_profile_password_path, method: :put + + .control-group + = f.label :password, 'New password' + .controls= f.password_field :password, required: true + .control-group + = f.label :password_confirmation + .controls + = f.password_field :password_confirmation, required: true + .form-actions + = f.submit 'Save password', class: "btn btn-save" diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 25bf7912f1e..ada2892c6ba 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -1,18 +1,14 @@ -= image_tag gravatar_icon(@user.email, 60), alt: '', class: 'avatar s60' %h3.page-title - = @user.name - %br - %small - = @user.email - - .pull-right - = link_to destroy_user_session_path, class: "logout", method: :delete do - %small - %i.icon-signout - Logout + Profile settings +%p.light + This information appears on your profile. + - if current_user.ldap_user? + Some options are unavailable for LDAP accounts %hr -= form_for @user, url: profile_path, method: :put, html: { class: "edit_user form-horizontal" } do |f| + + += form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit_user form-horizontal" } do |f| -if @user.errors.any? %div.alert.alert-error %ul @@ -29,7 +25,7 @@ = f.label :email, class: "control-label" .controls = f.text_field :email, class: "input-xlarge", required: true - %span.help-block We also use email for avatar detection. + %span.help-block We also use email for avatar detection if no avatar is uploaded. .control-group = f.label :skype, class: "control-label" .controls= f.text_field :skype, class: "input-xlarge" @@ -46,45 +42,23 @@ %span.help-block Tell us about yourself in fewer than 250 characters. .span5.pull-right - %fieldset.tips - %legend Tips: - %ul - %li - %p You can change your password on the Account page - - if Gitlab.config.gravatar.enabled - %li - %p You can change your avatar at #{link_to "gravatar.com", "http://gravatar.com"} - - - if Gitlab.config.omniauth.enabled && @user.provider? - %li - %p - You can login through #{@user.provider.titleize}! - = link_to "click here to change", account_profile_path - - if current_user.can_create_group? - %li - %p - Need a group for several dependent projects? - = link_to new_group_path, class: "btn btn-tiny" do - Create a group - - unless current_user.projects_limit_left > 100 - %fieldset - %legend - Personal projects: - %small.pull-right - %span= current_user.personal_projects.count - of - %span= current_user.projects_limit - .padded - .progress - .bar{style: "width: #{current_user.projects_limit_percent}%;"} + .light-well + = image_tag avatar_icon(@user.email, 160), alt: '', class: 'avatar s160' - %fieldset - %legend - SSH public keys: - %span.pull-right - = link_to pluralize(current_user.keys.count, 'key'), profile_keys_path - .padded - = link_to "Add Public Key", new_profile_key_path, class: "btn btn-small" + .clearfix + .profile-avatar-form-option + %p.light + You can upload an avatar here + %br + or change it at #{link_to "gravatar.com", "http://gravatar.com"} + %hr + %a.choose-btn.btn.btn-small.js-choose-user-avatar-button + %i.icon-paper-clip + %span Choose File ... + + %span.file_name.js-avatar-filename File name... + = f.file_field :avatar, class: "js-user-avatar-input hide" + %span.help-block The maximum file size allowed is 200KB. .form-actions = f.submit 'Save changes', class: "btn btn-save" diff --git a/app/views/profiles/update_username.js.haml b/app/views/profiles/update_username.js.haml index abd90269c93..249680bcab6 100644 --- a/app/views/profiles/update_username.js.haml +++ b/app/views/profiles/update_username.js.haml @@ -1,6 +1,6 @@ - if @user.valid? :plain - $('.update-username .update-success').show(); + new Flash("Username sucessfully changed", "notice") - else :plain - $('.update-username .update-failed').show(); + new Flash("Username change failed - #{@user.errors.full_messages.first}", "alert") diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index b8392525791..4372647a41c 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -24,7 +24,7 @@ %p = link_to project_commit_path(@project, commit.id), class: 'commit_short_id' do = commit.short_id - = image_tag gravatar_icon(commit.author_email), class: "avatar s16", alt: '' + = image_tag avatar_icon(commit.author_email), class: "avatar s16", alt: '' %span.light = gfm escape_once(truncate(commit.title, length: 40)) %span diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder index 46f9838e84a..27c8fa6da72 100644 --- a/app/views/projects/commits/show.atom.builder +++ b/app/views/projects/commits/show.atom.builder @@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link :href => project_commit_url(@project, :id => commit.id) xml.title truncate(commit.title, :length => 80) xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(commit.author_email) + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(commit.author_email) xml.author do |author| xml.name commit.author_name xml.email commit.author_email diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 117ee13140e..03cd3cbd658 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -67,7 +67,7 @@ - if Project.issues_tracker.values.count > 1 .control-group = f.label :issues_tracker, "Issues tracker", class: 'control-label' - .controls= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled }) + .controls= f.select(:issues_tracker, project_issues_trackers, {}, { disabled: !@project.issues_enabled }) .control-group = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label' diff --git a/app/views/projects/hooks/_data_ex.html.erb b/app/views/projects/hooks/_data_ex.html.erb index b4281fa18c7..5092aaf6750 100644 --- a/app/views/projects/hooks/_data_ex.html.erb +++ b/app/views/projects/hooks/_data_ex.html.erb @@ -5,6 +5,7 @@ "ref": "refs/heads/master", "user_id": 4, "user_name": "John Smith", + "project_id": 15, "repository": { "name": "Diaspora", "url": "git@localhost:diaspora.git", diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index b9a2c18efdc..effad718c80 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -6,6 +6,9 @@ .issue-title %span.light= "##{issue.iid}" = link_to_gfm truncate(issue.title, length: 100), project_issue_path(issue.project, issue), class: "row_title" + - if issue.closed? + %small.pull-right + = "CLOSED" .issue-info - if issue.assignee diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml index 539c45edd94..427d6533134 100644 --- a/app/views/projects/issues/_issues.html.haml +++ b/app/views/projects/issues/_issues.html.haml @@ -52,7 +52,7 @@ - @project.team.members.sort_by(&:name).each do |user| %li = link_to project_filter_path(assignee_id: user.id) do - = image_tag gravatar_icon(user.email), class: "avatar s16", alt: '' + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' = user.name .dropdown.inline.prepend-left-10 diff --git a/app/views/projects/issues/index.atom.builder b/app/views/projects/issues/index.atom.builder index 00ddd4bf702..012ba235951 100644 --- a/app/views/projects/issues/index.atom.builder +++ b/app/views/projects/issues/index.atom.builder @@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear xml.link :href => project_issue_url(@project, issue) xml.title truncate(issue.title, :length => 80) xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ") - xml.media :thumbnail, :width => "40", :height => "40", :url => gravatar_icon(issue.author_email) + xml.media :thumbnail, :width => "40", :height => "40", :url => avatar_icon(issue.author_email) xml.author do |author| xml.name issue.author_name xml.email issue.author_email diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index 2bd5a027a02..e256ee2153c 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -35,7 +35,7 @@ - @project.team.members.sort_by(&:name).each do |user| %li = link_to project_filter_path(assignee_id: user.id) do - = image_tag gravatar_icon(user.email), class: "avatar s16", alt: '' + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' = user.name .dropdown.inline.prepend-left-10 diff --git a/app/views/projects/milestones/_issues.html.haml b/app/views/projects/milestones/_issues.html.haml index bf81cfda45f..21939ad0132 100644 --- a/app/views/projects/milestones/_issues.html.haml +++ b/app/views/projects/milestones/_issues.html.haml @@ -8,4 +8,4 @@ = link_to_gfm truncate(issue.title, length: 40), [@project, issue] - if issue.assignee .pull-right - = image_tag gravatar_icon(issue.assignee.email, 16), class: "avatar s16" + = image_tag avatar_icon(issue.assignee.email, 16), class: "avatar s16" diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index b755a813419..dc7fd40d5e3 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -99,7 +99,7 @@ - @users.each do |user| %li = link_to user, title: user.name, class: "dark" do - = image_tag gravatar_icon(user.email, 32), class: "avatar s32" + = image_tag avatar_icon(user.email, 32), class: "avatar s32" %strong= truncate(user.name, lenght: 40) %br %small.cgray= user.username diff --git a/app/views/projects/network/show.json.erb b/app/views/projects/network/show.json.erb index f0bedcf2d35..dc82adcb2c6 100644 --- a/app/views/projects/network/show.json.erb +++ b/app/views/projects/network/show.json.erb @@ -9,7 +9,7 @@ author: { name: c.author_name, email: c.author_email, - icon: gravatar_icon(c.author_email, 20) + icon: avatar_icon(c.author_email, 20) }, time: c.time, space: c.spaces.first, diff --git a/app/views/projects/notes/_discussion.html.haml b/app/views/projects/notes/_discussion.html.haml index a964d86a8dc..e0aad0cc956 100644 --- a/app/views/projects/notes/_discussion.html.haml +++ b/app/views/projects/notes/_discussion.html.haml @@ -8,7 +8,7 @@ = link_to "javascript:;", class: "js-details-target turn-off js-toggler-target" do %i.icon-eye-open Show discussion - = image_tag gravatar_icon(note.author_email), class: "avatar s32" + = image_tag avatar_icon(note.author_email), class: "avatar s32" %div = link_to_member(@project, note.author, avatar: false) - if note.for_merge_request? diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 324b698f3b5..e56e9153c2f 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -13,7 +13,7 @@ = 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 %i.icon-trash.cred Remove - = image_tag gravatar_icon(note.author_email), class: "avatar s32" + = image_tag avatar_icon(note.author_email), class: "avatar s32" = link_to_member(@project, note.author, avatar: false) %span.note-last-update = note_timestamp(note) diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml index faa3ed1746c..6e537d2959b 100644 --- a/app/views/projects/repositories/_feed.html.haml +++ b/app/views/projects/repositories/_feed.html.haml @@ -11,7 +11,7 @@ %div = link_to project_commits_path(@project, commit.id) do %code= commit.short_id - = image_tag gravatar_icon(commit.author_email), class: "", width: 16, alt: '' + = image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: '' = gfm escape_once(truncate(commit.title, length: 40)) %td %span.pull-right.cgray diff --git a/app/views/projects/repositories/stats.html.haml b/app/views/projects/repositories/stats.html.haml index 454296e82fd..679b4211cde 100644 --- a/app/views/projects/repositories/stats.html.haml +++ b/app/views/projects/repositories/stats.html.haml @@ -19,7 +19,7 @@ %ol.styled - @stats.authors[0...50].each do |author| %li - = image_tag gravatar_icon(author.email, 16), class: 'avatar s16', alt: '' + = image_tag avatar_icon(author.email, 16), class: 'avatar s16', alt: '' = author.name %small.light= author.email .pull-right diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 06ca5169dff..aefcd833952 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -17,7 +17,7 @@ %p %p %span.light Repo size is - #{@project.repository.size} MB + = repository_size %p %span.light Created at #{@project.created_at.stamp('Aug 22, 2013')} diff --git a/app/views/projects/snippets/_snippet.html.haml b/app/views/projects/snippets/_snippet.html.haml index fc1c0893b08..6185e35cf6f 100644 --- a/app/views/projects/snippets/_snippet.html.haml +++ b/app/views/projects/snippets/_snippet.html.haml @@ -16,6 +16,6 @@ = "##{snippet.id}" %span by - = image_tag gravatar_icon(snippet.author_email), class: "avatar avatar-inline s16" + = image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16" = snippet.author_name %span.light #{time_ago_in_words(snippet.created_at)} ago diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml index 4a07ebf7fd9..ac32f4866b6 100644 --- a/app/views/projects/snippets/show.html.haml +++ b/app/views/projects/snippets/show.html.haml @@ -5,7 +5,7 @@ = "##{@snippet.id}" %span.light by - = image_tag gravatar_icon(@snippet.author_email), class: "avatar avatar-inline s16" + = image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16" = @snippet.author_name %div= render 'projects/snippets/blob' %div#notes= render "projects/notes/notes_with_form" diff --git a/app/views/projects/team_members/_team_member.html.haml b/app/views/projects/team_members/_team_member.html.haml index 916cf2e7a87..5c93e6e3eae 100644 --- a/app/views/projects/team_members/_team_member.html.haml +++ b/app/views/projects/team_members/_team_member.html.haml @@ -9,7 +9,7 @@ = 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 %i.icon-minus.icon-white - = image_tag gravatar_icon(user.email, 32), class: "avatar s32" + = image_tag avatar_icon(user.email, 32), class: "avatar s32" %p %strong= user.name %span.cgray= user.username diff --git a/app/views/projects/tree/_submodule_item.html.haml b/app/views/projects/tree/_submodule_item.html.haml index 26aad16e2c0..badc7d992bd 100644 --- a/app/views/projects/tree/_submodule_item.html.haml +++ b/app/views/projects/tree/_submodule_item.html.haml @@ -1,11 +1,10 @@ -- url = submodule_item.url(@ref) rescue '' -- name = submodule_item.basename -- return '' unless url -%tr{ class: "tree-item", url: url } +%tr{ class: "tree-item" } %td.tree-item-file-name = image_tag "submodule.png" - %span= truncate(name, length: 40) + %span + = link_to truncate(submodule_item.name, length: 40), submodule_item.submodule_url + @ + %span.monospace #{submodule_item.id[0..10]} + %td + %td %td - %code= submodule_item.id[0..10] - %td{ colspan: 2 } - = link_to truncate(url, length: 40), url diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml index 9689c9c4d38..8514bc3ddd0 100644 --- a/app/views/snippets/_snippet.html.haml +++ b/app/views/snippets/_snippet.html.haml @@ -18,6 +18,6 @@ %span by = link_to user_snippets_path(snippet.author) do - = image_tag gravatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: '' + = image_tag avatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: '' = snippet.author_name %span.light #{time_ago_in_words(snippet.created_at)} ago diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml index 37f9e7576f5..a680e5eb5b7 100644 --- a/app/views/snippets/show.html.haml +++ b/app/views/snippets/show.html.haml @@ -17,7 +17,7 @@ %span.light by = link_to user_snippets_path(@snippet.author) do - = image_tag gravatar_icon(@snippet.author_email), class: "avatar avatar-inline s16" + = image_tag avatar_icon(@snippet.author_email), class: "avatar avatar-inline s16" = @snippet.author_name .back-link diff --git a/app/views/snippets/user_index.html.haml b/app/views/snippets/user_index.html.haml index 49636c3f5f0..1cb53ec6a25 100644 --- a/app/views/snippets/user_index.html.haml +++ b/app/views/snippets/user_index.html.haml @@ -1,5 +1,5 @@ %h3.page-title - = image_tag gravatar_icon(@user.email), class: "avatar s24" + = image_tag avatar_icon(@user.email), class: "avatar s24" = @user.name %span \/ diff --git a/app/views/users/_projects.html.haml b/app/views/users/_projects.html.haml index f1b2c8dd7f7..a61c6ba5b86 100644 --- a/app/views/users/_projects.html.haml +++ b/app/views/users/_projects.html.haml @@ -3,9 +3,4 @@ %ul.well-list - @projects.each do |project| %li - = link_to project_path(project), class: dom_class(project) do - - if project.namespace - = project.namespace.human_name - \/ - %strong.well-title - = truncate(project.name, length: 45) + = link_to_project project diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 743ab0949a1..53a0a9232a4 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -1,13 +1,13 @@ .row .span8 %h3.page-title - = image_tag gravatar_icon(@user.email, 90), class: "avatar s90", alt: '' + = image_tag avatar_icon(@user.email, 90), class: "avatar s90", alt: '' = @user.name - if @user == current_user .pull-right = link_to profile_path, class: 'btn' do %i.icon-edit - Edit Profile + Edit Profile settings %br %small #{@user.username} %br diff --git a/app/views/users_groups/_users_group.html.haml b/app/views/users_groups/_users_group.html.haml index 5cdb5bb8c40..5934ff72e8e 100644 --- a/app/views/users_groups/_users_group.html.haml +++ b/app/views/users_groups/_users_group.html.haml @@ -1,7 +1,7 @@ - user = member.user - return unless user %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)} - = image_tag gravatar_icon(user.email, 16), class: "avatar s16" + = image_tag avatar_icon(user.email, 16), class: "avatar s16" %strong= user.name %span.cgray= user.username - if user == current_user diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 28578eee50a..163af226aaa 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -73,6 +73,7 @@ production: &base ## External issues trackers issues_tracker: # redmine: + # title: "Redmine" # ## If not nil, link 'Issues' on project page will be replaced with this # ## Use placeholders: # ## :project_id - GitLab project identifier @@ -93,6 +94,7 @@ production: &base # new_issue_url: "http://redmine.sample/projects/:issues_tracker_id/issues/new" # # jira: + # title: "Atlassian Jira" # project_url: "http://jira.sample/issues/?jql=project=:issues_tracker_id" # issues_url: "http://jira.sample/browse/:id" # new_issue_url: "http://jira.sample/secure/CreateIssue.jspa" @@ -206,6 +208,7 @@ test: <<: *base issues_tracker: redmine: + title: "Redmine" project_url: "http://redmine/projects/:issues_tracker_id" issues_url: "http://redmine/:project_id/:issues_tracker_id/:id" new_issue_url: "http://redmine/projects/:issues_tracker_id/issues/new" diff --git a/config/initializers/devise_async.rb b/config/initializers/devise_async.rb new file mode 100644 index 00000000000..05a1852cdbd --- /dev/null +++ b/config/initializers/devise_async.rb @@ -0,0 +1 @@ +Devise::Async.backend = :sidekiq diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index 52a099c3e16..501cad4a838 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -1,11 +1,9 @@ # Be sure to restart your server when you modify this file. -Gitlab::Application.config.session_store :cookie_store, key: '_gitlab_session', - secure: Gitlab::Application.config.force_ssl, - httponly: true, - path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root - -# Use the database for sessions instead of the cookie-based default, -# which shouldn't be used to store highly confidential information -# (create the session table with "rails generate session_migration") -# Gitlab::Application.config.session_store :active_record_store +Gitlab::Application.config.session_store( + :redis_store, # Using the cookie_store would enable session replay attacks. + key: '_gitlab_session', + secure: Gitlab::Application.config.force_ssl, + httponly: true, + path: (Rails.application.config.relative_url_root.nil?) ? '/' : Rails.application.config.relative_url_root +) diff --git a/config/routes.rb b/config/routes.rb index 612a7327ec5..6d7358608ea 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,19 +99,21 @@ Gitlab::Application.routes.draw do # resource :profile, only: [:show, :update] do member do - get :account get :history - get :token get :design - put :update_password put :reset_private_token put :update_username end scope module: :profiles do + resource :account, only: [:show, :update] resource :notifications, only: [:show, :update] - resource :password, only: [:new, :create] + resource :password, only: [:new, :create, :edit, :update] do + member do + put :reset + end + end resources :keys resources :groups, only: [:index] do member do diff --git a/db/migrate/20131005191208_add_avatar_to_users.rb b/db/migrate/20131005191208_add_avatar_to_users.rb new file mode 100644 index 00000000000..7b4de37ad72 --- /dev/null +++ b/db/migrate/20131005191208_add_avatar_to_users.rb @@ -0,0 +1,5 @@ +class AddAvatarToUsers < ActiveRecord::Migration + def change + add_column :users, :avatar, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 713d9f733d6..b3bc31c76dd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130926081215) do +ActiveRecord::Schema.define(:version => 20131005191208) do create_table "deploy_keys_projects", :force => true do |t| t.integer "deploy_key_id", :null => false @@ -283,6 +283,7 @@ ActiveRecord::Schema.define(:version => 20130926081215) do t.integer "notification_level", :default => 1, :null => false t.datetime "password_expires_at" t.integer "created_by_id" + t.string "avatar" end add_index "users", ["admin"], :name => "index_users_on_admin" diff --git a/doc/api/projects.md b/doc/api/projects.md index 0f73fb434da..af9e682c24f 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -244,6 +244,18 @@ Parameters: + `public` (optional) +## Remove project + +Removes project with all resources(issues, merge requests etc) + +``` +DELETE /projects/:id +``` + +Parameters: + ++ `id` (required) - The ID of a project + ## Team members diff --git a/doc/api/repositories.md b/doc/api/repositories.md index cb0626972e5..2769c22d6aa 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -356,3 +356,16 @@ Parameters: + `id` (required) - The ID of a project + `sha` (required) - The commit or branch name + `filepath` (required) - The path the file + + +## Get file archive + +Get a an archive of the repository + +``` +GET /projects/:id/repository/archive +``` + +Parameters: ++ `id` (required) - The ID of a project ++ `sha` (optional) - The commit sha to download defaults to the tip of the default branch
\ No newline at end of file diff --git a/doc/api/system_hooks.md b/doc/api/system_hooks.md index 5eeb3652d57..355ce31c126 100644 --- a/doc/api/system_hooks.md +++ b/doc/api/system_hooks.md @@ -1,5 +1,7 @@ All methods require admin authorization.
+The url endpoint of the system hooks can be configured in [the admin area under hooks](/admin/hooks).
+
## List system hooks
Get list of system hooks
diff --git a/doc/install/installation.md b/doc/install/installation.md index 933799776bd..7c6a2db9734 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -63,7 +63,7 @@ Make sure you have the right version of Python installed. python --version # If it's Python 3 you might need to install Python 2 separately - sudo apt-get install python2.7 + sudo apt-get install -y python2.7 # Make sure you can access Python via python2 python2 --version @@ -72,7 +72,7 @@ Make sure you have the right version of Python installed. sudo ln -s /usr/bin/python /usr/bin/python2 # For reStructuredText markup language support install required package: - sudo apt-get install python-docutils + sudo apt-get install -y python-docutils **Note:** In order to receive mail notifications, make sure to install a mail server. By default, Debian is shipped with exim4 whereas Ubuntu diff --git a/features/profile/profile.feature b/features/profile/profile.feature index c74b0993fb3..6198fd2b306 100644 --- a/features/profile/profile.feature +++ b/features/profile/profile.feature @@ -12,26 +12,31 @@ Feature: Profile And I should see new contact info Scenario: I change my password without old one - Given I visit profile account page + Given I visit profile password page When I try change my password w/o old one Then I should see a missing password error message - And I should be redirected to account page + And I should be redirected to password page Scenario: I change my password - Given I visit profile account page + Given I visit profile password page Then I change my password And I should be redirected to sign in page + Scenario: I edit my avatar + Given I visit profile page + Then I change my avatar + And I should see new avatar + Scenario: My password is expired Given my password is expired And I am not an ldap user - And I visit profile account page + Given I visit profile password page Then I redirected to expired password page And I submit new password And I redirected to sign in page Scenario: I unsuccessfully change my password - Given I visit profile account page + Given I visit profile password page When I unsuccessfully change my password Then I should see a password error message diff --git a/features/project/source/markdown_render.feature b/features/project/source/markdown_render.feature new file mode 100644 index 00000000000..a7a9cee7b0d --- /dev/null +++ b/features/project/source/markdown_render.feature @@ -0,0 +1,75 @@ +Feature: Project markdown render + Background: + Given I sign in as a user + And I own project "Delta" + Given I visit project source page + + Scenario: I browse files from master branch + Then I should see files from repository in master + And I should see rendered README which contains correct links + And I click on Gitlab API in README + Then I should see correct document rendered + + Scenario: I view README in master branch + Then I should see files from repository in master + And I should see rendered README which contains correct links + And I click on Rake tasks in README + Then I should see correct directory rendered + + Scenario: I navigate to doc directory to view documentation in master + And I navigate to the doc/api/README + And I see correct file rendered + And I click on users in doc/api/README + Then I should see the correct document file + + Scenario: I navigate to doc directory to view user doc in master + And I navigate to the doc/api/README + And I see correct file rendered + And I click on raketasks in doc/api/README + Then I should see correct directory rendered + + Scenario: I browse files from markdown branch + When I visit markdown branch + Then I should see files from repository in markdown branch + And I should see rendered README which contains correct links + And I click on Gitlab API in README + Then I should see correct document rendered for markdown branch + + Scenario: I browse directory from markdown branch + When I visit markdown branch + Then I should see files from repository in markdown branch + And I should see rendered README which contains correct links + And I click on Rake tasks in README + Then I should see correct directory rendered for markdown branch + + Scenario: I navigate to doc directory to view documentation in markdown branch + When I visit markdown branch + And I navigate to the doc/api/README + And I see correct file rendered in markdown branch + And I click on users in doc/api/README + Then I should see the users document file in markdown branch + + Scenario: I navigate to doc directory to view user doc in markdown branch + When I visit markdown branch + And I navigate to the doc/api/README + And I see correct file rendered in markdown branch + And I click on raketasks in doc/api/README + Then I should see correct directory rendered for markdown branch + + Scenario: I create a wiki page with different links + Given I go to wiki page + And I add various links to the wiki page + Then Wiki page should have added links + And I click on test link + Then I see new wiki page named test + When I go back to wiki page home + And I click on GitLab API doc link + Then I see Gitlab API document + When I go back to wiki page home + And I click on Rake tasks link + Then I see Rake tasks directory + + Scenario: I visit the help page with markdown + Given I visit to the help page + And I select a page with markdown + Then I should see a help page with markdown
\ No newline at end of file diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index 5b2a6321265..753e2c19bcb 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -3,9 +3,7 @@ class Profile < Spinach::FeatureSteps include SharedPaths step 'I should see my profile info' do - page.should have_content "Profile" - page.should have_content @user.name - page.should have_content @user.email + page.should have_content "Profile settings" end step 'I change my contact info' do @@ -22,6 +20,17 @@ class Profile < Spinach::FeatureSteps @user.twitter.should == 'testtwitter' end + step 'I change my avatar' do + attach_file(:user_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png')) + click_button "Save changes" + @user.reload + end + + step 'I should see new avatar' do + @user.avatar.should be_instance_of AttachmentUploader + @user.avatar.url.should == "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png" + end + step 'I try change my password w/o old one' do within '.update-password' do fill_in "user_password", with: "222333" @@ -124,8 +133,12 @@ class Profile < Spinach::FeatureSteps current_path.should == new_user_session_path end + step 'I should be redirected to password page' do + current_path.should == edit_profile_password_path + end + step 'I should be redirected to account page' do - current_path.should == account_profile_path + current_path.should == profile_account_path end step 'I click on my profile picture' do diff --git a/features/steps/project/project_markdown_render.rb b/features/steps/project/project_markdown_render.rb new file mode 100644 index 00000000000..951c831838d --- /dev/null +++ b/features/steps/project/project_markdown_render.rb @@ -0,0 +1,165 @@ +class Spinach::Features::ProjectMarkdownRender < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + + And 'I own project "Delta"' do + @project = Project.find_by_name "Delta" + @project ||= create(:project_with_code, name: "Delta", namespace: @user.namespace) + @project.team << [@user, :master] + end + + Then 'I should see files from repository in master' do + current_path.should == project_tree_path(@project, "master") + page.should have_content "Gemfile" + page.should have_content "app" + page.should have_content "README" + end + + And 'I should see rendered README which contains correct links' do + page.should have_content "Welcome to GitLab GitLab is a free project and repository management application" + page.should have_link "GitLab API doc" + page.should have_link "GitLab API website" + page.should have_link "Rake tasks" + page.should have_link "backup and restore procedure" + end + + And 'I click on Gitlab API in README' do + click_link "GitLab API doc" + end + + Then 'I should see correct document rendered' do + current_path.should == project_blob_path(@project, "master/doc/api/README.md") + page.should have_content "All API requests require authentication" + end + + And 'I click on Rake tasks in README' do + click_link "Rake tasks" + end + + Then 'I should see correct directory rendered' do + current_path.should == project_tree_path(@project, "master/doc/raketasks") + page.should have_content "backup_restore.md" + page.should have_content "maintenance.md" + end + + And 'I navigate to the doc/api/README' do + click_link "doc" + click_link "api" + click_link "README.md" + end + + And 'I see correct file rendered' do + current_path.should == project_blob_path(@project, "master/doc/api/README.md") + page.should have_content "Contents" + page.should have_link "Users" + page.should have_link "Rake tasks" + end + + And 'I click on users in doc/api/README' do + click_link "Users" + end + + Then 'I should see the correct document file' do + current_path.should == project_blob_path(@project, "master/doc/api/users.md") + page.should have_content "Get a list of users." + end + + And 'I click on raketasks in doc/api/README' do + click_link "Rake tasks" + end + + When 'I visit markdown branch' do + visit project_tree_path(@project, "markdown") + end + + Then 'I should see files from repository in markdown branch' do + current_path.should == project_tree_path(@project, "markdown") + page.should have_content "Gemfile" + page.should have_content "app" + page.should have_content "README" + end + + And 'I see correct file rendered in markdown branch' do + current_path.should == project_blob_path(@project, "markdown/doc/api/README.md") + page.should have_content "Contents" + page.should have_link "Users" + page.should have_link "Rake tasks" + end + + Then 'I should see correct document rendered for markdown branch' do + current_path.should == project_blob_path(@project, "markdown/doc/api/README.md") + page.should have_content "All API requests require authentication" + end + + Then 'I should see correct directory rendered for markdown branch' do + current_path.should == project_tree_path(@project, "markdown/doc/raketasks") + page.should have_content "backup_restore.md" + page.should have_content "maintenance.md" + end + + Then 'I should see the users document file in markdown branch' do + current_path.should == project_blob_path(@project, "markdown/doc/api/users.md") + page.should have_content "Get a list of users." + end + + Given 'I go to wiki page' do + click_link "Wiki" + current_path.should == project_wiki_path(@project, "home") + end + + And 'I add various links to the wiki page' do + fill_in "wiki[content]", with: "[test](test)\n[GitLab API doc](doc/api/README.md)\n[Rake tasks](doc/raketasks)\n" + fill_in "wiki[message]", with: "Adding links to wiki" + click_button "Create page" + end + + Then 'Wiki page should have added links' do + current_path.should == project_wiki_path(@project, "home") + page.should have_content "test GitLab API doc Rake tasks" + end + + And 'I click on test link' do + click_link "test" + end + + Then 'I see new wiki page named test' do + current_path.should == project_wiki_path(@project, "test") + page.should have_content "Editing page" + end + + When 'I go back to wiki page home' do + visit project_wiki_path(@project, "home") + current_path.should == project_wiki_path(@project, "home") + end + + And 'I click on GitLab API doc link' do + click_link "GitLab API" + end + + Then 'I see Gitlab API document' do + current_path.should == project_blob_path(@project, "master/doc/api/README.md") + page.should have_content "Status codes" + end + + And 'I click on Rake tasks link' do + click_link "Rake tasks" + end + + Then 'I see Rake tasks directory' do + current_path.should == project_tree_path(@project, "master/doc/raketasks") + page.should have_content "backup_restore.md" + page.should have_content "maintenance.md" + end + + Given 'I visit to the help page' do + visit help_path + end + + And 'I select a page with markdown' do + click_link "Rake Tasks" + end + + Then 'I should see a help page with markdown' do + page.should have_content "GitLab provides some specific rake tasks to enable special features or perform maintenance tasks" + end +end
\ No newline at end of file diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index c30eccce1c5..156fa5bab4e 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -65,8 +65,12 @@ module SharedPaths visit profile_path end + step 'I visit profile password page' do + visit edit_profile_password_path + end + step 'I visit profile account page' do - visit account_profile_path + visit profile_account_path end step 'I visit profile SSH keys page' do diff --git a/lib/api/groups.rb b/lib/api/groups.rb index 265417fd6bc..290b78d8017 100644 --- a/lib/api/groups.rb +++ b/lib/api/groups.rb @@ -66,7 +66,6 @@ module API present group, with: Entities::GroupDetail end - # Remove group # # Parameters: diff --git a/lib/api/projects.rb b/lib/api/projects.rb index cf357b23c40..221f1f1e23c 100644 --- a/lib/api/projects.rb +++ b/lib/api/projects.rb @@ -129,6 +129,16 @@ module API end end + # Remove project + # + # Parameters: + # id (required) - The ID of a project + # Example Request: + # DELETE /projects/:id + delete ":id" do + authorize! :remove_project, user_project + user_project.destroy + end # Mark this project as forked from another # diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index 1a911eae2bb..c9422fdb165 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -144,7 +144,7 @@ module API trees = [] %w(trees blobs submodules).each do |type| - trees += tree.send(type).map { |t| { name: t.name, type: type.singularize, mode: t.mode, id: t.id } } + trees += tree.send(type).map { |t| {name: t.name, type: type.singularize, mode: t.mode, id: t.id} } end trees @@ -176,6 +176,34 @@ module API content_type blob.mime_type present blob.data end + + # Get a an archive of the repository + # + # Parameters: + # id (required) - The ID of a project + # sha (optional) - the commit sha to download defaults to the tip of the default branch + # Example Request: + # GET /projects/:id/repository/archive + get ":id/repository/archive" do + authorize! :download_code, user_project + repo = user_project.repository + ref = params[:sha] + storage_path = Rails.root.join("tmp", "repositories") + + file_path = repo.archive_repo(ref, storage_path) + if file_path && File.exists?(file_path) + data = File.open(file_path, 'rb').read + + header "Content-Disposition:", " infile; filename=\"#{File.basename(file_path)}\"" + content_type 'application/x-gzip' + + env['api.format'] = :binary + + present data + else + not_found! + end + end end end end diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index c5e3d049fd7..252201f11be 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -28,7 +28,9 @@ module Backup if File.exists?(path_to_repo(wiki)) print " * #{wiki.path_with_namespace} ... " - if system("cd #{path_to_repo(wiki)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(wiki)} --all > /dev/null 2>&1") + if wiki.empty? + puts " [SKIPPED]".cyan + elsif system("cd #{path_to_repo(wiki)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(wiki)} --all > /dev/null 2>&1") puts " [DONE]".green else puts " [FAILED]".red diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb index d9c2d3b626d..b84c005524f 100644 --- a/lib/redcarpet/render/gitlab_html.rb +++ b/lib/redcarpet/render/gitlab_html.rb @@ -6,6 +6,8 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML def initialize(template, options = {}) @template = template @project = @template.instance_variable_get("@project") + @ref = @template.instance_variable_get("@ref") + @request_path = @template.instance_variable_get("@path") super options end @@ -32,7 +34,19 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML h.link_to_gfm(content, link, title: title) end + def preprocess(full_document) + if @project + h.create_relative_links(full_document, @project.path_with_namespace, @ref, @request_path, is_wiki?) + else + full_document + end + end + def postprocess(full_document) h.gfm(full_document) end + + def is_wiki? + @template.instance_variable_get("@wiki") + end end diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index 6aff7b5a8f9..f157d71b14f 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -218,9 +218,7 @@ reload(){ kill -USR2 "$wpid" echo "Done." echo "Restarting GitLab Sidekiq since it isn't capable of reloading its config..." - RAILS_ENV=$RAILS_ENV bundle exec rake sidekiq:stop - echo "Starting Sidekiq..." - RAILS_ENV=$RAILS_ENV bundle exec rake sidekiq:start + RAILS_ENV=$RAILS_ENV bundle exec rake sidekiq:restart # Waiting 2 seconds for sidekiq to write it. sleep 2 status diff --git a/lib/tasks/sidekiq.rake b/lib/tasks/sidekiq.rake index d0e9dfe46a1..ba79b6e035d 100644 --- a/lib/tasks/sidekiq.rake +++ b/lib/tasks/sidekiq.rake @@ -5,16 +5,28 @@ namespace :sidekiq do end desc "GITLAB | Start sidekiq" - task :start do - system "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &" + task :start => :restart + + desc 'GitLab | Restart sidekiq' + task :restart do + if File.exist?(pidfile) + puts 'Shutting down existing sidekiq process.' + Rake::Task['sidekiq:stop'].invoke + puts 'Starting new sidekiq process.' + end + system "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} -d -L #{log_file} >> #{log_file} 2>&1" end desc "GITLAB | Start sidekiq with launchd on Mac OS X" task :launchd do - system "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1" + system "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{log_file} 2>&1" end def pidfile Rails.root.join("tmp", "pids", "sidekiq.pid") end + + def log_file + Rails.root.join("log", "sidekiq.log") + end end diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index ba580d9484d..d29bed4dea7 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -216,12 +216,6 @@ describe "On a merge request diff", js: true, focus: true do end end - it do - within("tr[id='342e16cbbd482ac2047dc679b2749d248cc1428f_18_17'] + .js-temp-notes-holder") do - should have_no_css(".js-temp-notes-holder") - end - end - it 'should be added as discussion' do should have_content("Another comment on line 17") should have_css(".notes_holder") diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb index 80c9f5d7f14..b67ce3c67f1 100644 --- a/spec/features/profile_spec.rb +++ b/spec/features/profile_spec.rb @@ -12,7 +12,7 @@ describe "Profile account page" do describe "when signup is enabled" do before do Gitlab.config.gitlab.stub(:signup_enabled).and_return(true) - visit account_profile_path + visit profile_account_path end it { page.should have_content("Remove account") } @@ -26,12 +26,12 @@ describe "Profile account page" do describe "when signup is disabled" do before do Gitlab.config.gitlab.stub(:signup_enabled).and_return(false) - visit account_profile_path + visit profile_account_path end it "should not have option to remove account" do page.should_not have_content("Remove account") - current_path.should == account_profile_path + current_path.should == profile_account_path end end end diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb index 7754b28347a..078c257538f 100644 --- a/spec/features/security/profile_access_spec.rb +++ b/spec/features/security/profile_access_spec.rb @@ -29,7 +29,7 @@ describe "Users Security" do end describe "GET /profile/account" do - subject { account_profile_path } + subject { profile_account_path } it { should be_allowed_for @u1 } it { should be_allowed_for :admin } diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb index 229f49659cf..0d066be5b45 100644 --- a/spec/helpers/application_helper_spec.rb +++ b/spec/helpers/application_helper_spec.rb @@ -38,6 +38,24 @@ describe ApplicationHelper do current_action?(:baz, :bar, :foo).should be_true end end + + describe "avatar_icon" do + avatar_file_path = File.join(Rails.root, 'public', 'gitlab_logo.png') + + it "should return an url for the avatar" do + user = create(:user) + user.avatar = File.open(avatar_file_path) + user.save! + avatar_icon(user.email).to_s.should == "/uploads/user/avatar/#{ user.id }/gitlab_logo.png" + end + + it "should call gravatar_icon when no avatar is present" do + user = create(:user) + user.save! + stub!(:gravatar_icon).and_return('gravatar_method_called') + avatar_icon(user.email).to_s.should == "gravatar_method_called" + end + end describe "gravatar_icon" do let(:user_email) { 'user@email.com' } diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb index d49247accc2..a0bbc026421 100644 --- a/spec/helpers/gitlab_markdown_helper_spec.rb +++ b/spec/helpers/gitlab_markdown_helper_spec.rb @@ -406,6 +406,30 @@ describe GitlabMarkdownHelper do it "should generate absolute urls for emoji" do markdown(":smile:").should include("src=\"#{url_to_image("emoji/smile")}") end + + it "should handle relative urls for a file in master" do + actual = "[GitLab API doc](doc/api/README.md)\n" + expected = "<p><a href=\"/#{project.path_with_namespace}/blob/master/doc/api/README.md\">GitLab API doc</a></p>\n" + markdown(actual).should match(expected) + end + + it "should handle relative urls for a directory in master" do + actual = "[GitLab API doc](doc/api)\n" + expected = "<p><a href=\"/#{project.path_with_namespace}/tree/master/doc/api\">GitLab API doc</a></p>\n" + markdown(actual).should match(expected) + end + + it "should handle absolute urls" do + actual = "[GitLab](https://www.gitlab.com)\n" + expected = "<p><a href=\"https://www.gitlab.com\">GitLab</a></p>\n" + markdown(actual).should match(expected) + end + + it "should handle wiki urls" do + actual = "[Link](test/link)\n" + expected = "<p><a href=\"/#{project.path_with_namespace}/wikis/test/link\">Link</a></p>\n" + markdown(actual).should match(expected) + end end describe "#render_wiki_content" do diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb new file mode 100644 index 00000000000..62f88dd522b --- /dev/null +++ b/spec/helpers/projects_helper_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe ProjectsHelper do + describe '#project_issues_trackers' do + it "returns the correct issues trackers available" do + project_issues_trackers.should == + "<option value=\"redmine\">Redmine</option>\n" \ + "<option value=\"gitlab\">GitLab</option>" + end + end +end diff --git a/spec/lib/gitlab/satellite/merge_action_spec.rb b/spec/lib/gitlab/satellite/merge_action_spec.rb index 3be14383e06..e40ff73b7f0 100644 --- a/spec/lib/gitlab/satellite/merge_action_spec.rb +++ b/spec/lib/gitlab/satellite/merge_action_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe 'Gitlab::Satellite::MergeAction' do before(:each) do # TestEnv.init(mailer: false, init_repos: true, repos: true) - @master = ['master', 'bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a'] + @master = ['master', 'b1e6a9dbf1c85e6616497a5e7bad9143a4bd0828'] @one_after_stable = ['stable', '6ea87c47f0f8a24ae031c3fff17bc913889ecd00'] #this commit sha is one after stable @wiki_branch = ['wiki', '635d3e09b72232b6e92a38de6cc184147e5bcb41'] #this is the commit sha where the wiki branch goes off from master @conflicting_metior = ['metior', '313d96e42b313a0af5ab50fa233bf43e27118b3f'] #this branch conflicts with the wiki branch diff --git a/spec/models/gollum_wiki_spec.rb b/spec/models/gollum_wiki_spec.rb index aa850dfd0a3..9e07d9ee191 100644 --- a/spec/models/gollum_wiki_spec.rb +++ b/spec/models/gollum_wiki_spec.rb @@ -86,6 +86,27 @@ describe GollumWiki do end end + describe "#empty?" do + context "when the wiki repository is empty" do + before do + Gitlab::Shell.any_instance.stub(:add_repository) do + create_temp_repo("#{Rails.root}/tmp/test-git-base-path/non-existant.wiki.git") + end + project.stub(:path_with_namespace).and_return("non-existant") + end + + its(:empty?) { should be_true } + end + + context "when the wiki has pages" do + before do + create_page("index", "This is an awesome new Gollum Wiki") + end + + its(:empty?) { should be_false } + end + end + describe "#pages" do before do create_page("index", "This is an awesome new Gollum Wiki") diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 47ae760a7ed..dcaee39fa68 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -132,17 +132,17 @@ describe Project do it "should close merge request if last commit from source branch was pushed to target branch" do @merge_request.reloaded_commits - @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" - project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/stable", @key.user) + @merge_request.last_commit.id.should == "b1e6a9dbf1c85e6616497a5e7bad9143a4bd0828" + project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "b1e6a9dbf1c85e6616497a5e7bad9143a4bd0828", "refs/heads/stable", @key.user) @merge_request.reload @merge_request.merged?.should be_true end it "should update merge request commits with new one if pushed to source branch" do @merge_request.last_commit.should == nil - project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a", "refs/heads/master", @key.user) + project.update_merge_requests("8716fc78f3c65bbf7bcf7b574febd583bc5d2812", "b1e6a9dbf1c85e6616497a5e7bad9143a4bd0828", "refs/heads/master", @key.user) @merge_request.reload - @merge_request.last_commit.id.should == "bcf03b5de6c33f3869ef70d68cf06e679d1d7f9a" + @merge_request.last_commit.id.should == "b1e6a9dbf1c85e6616497a5e7bad9143a4bd0828" end end diff --git a/spec/observers/users_project_observer_spec.rb b/spec/observers/users_project_observer_spec.rb index e33d8cc50fd..e7c624fce59 100644 --- a/spec/observers/users_project_observer_spec.rb +++ b/spec/observers/users_project_observer_spec.rb @@ -65,4 +65,30 @@ describe UsersProjectObserver do @users_project.destroy end end -end + + describe "#after_create" do + context 'wiki_enabled creates repository directory' do + context 'wiki_enabled true creates wiki repository directory' do + before do + @project = create(:project, wiki_enabled: true) + @path = GollumWiki.new(@project, user).send(:path_to_repo) + end + + after do + FileUtils.rm_rf(@path) + end + + it { File.exists?(@path).should be_true } + end + + context 'wiki_enabled false does not create wiki repository directory' do + before do + @project = create(:project, wiki_enabled: false) + @path = GollumWiki.new(@project, user).send(:path_to_repo) + end + + it { File.exists?(@path).should be_false } + end + end + end +end
\ No newline at end of file diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index b8c0b6f33ed..bf4a1749418 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -730,4 +730,42 @@ describe API::API do end end end + + describe "DELETE /projects/:id" do + context "when authenticated as user" do + it "should remove project" do + delete api("/projects/#{project.id}", user) + response.status.should == 200 + end + + it "should not remove a project if not an owner" do + user3 = create(:user) + project.team << [user3, :developer] + delete api("/projects/#{project.id}", user3) + response.status.should == 403 + end + + it "should not remove a non existing project" do + delete api("/projects/1328", user) + response.status.should == 404 + end + + it "should not remove a project not attached to user" do + delete api("/projects/#{project.id}", user2) + response.status.should == 404 + end + end + + context "when authenticated as admin" do + it "should remove any existing project" do + delete api("/projects/#{project.id}", admin) + response.status.should == 200 + end + + it "should not remove a non existing project" do + delete api("/projects/1328", admin) + response.status.should == 404 + end + end + end end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index 2e509ea2933..9649c4d09c8 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -225,4 +225,16 @@ describe API::API do end end + describe "GET /projects/:id/repository/archive/:sha" do + it "should get the archive" do + get api("/projects/#{project.id}/repository/archive", user) + response.status.should == 200 + response.content_type.should == 'application/x-gzip' + end + + it "should return 404 for invalid sha" do + get api("/projects/#{project.id}/repository/archive/?sha=xxx", user) + response.status.should == 404 + end + end end diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb index 946ef7c28cb..1b1d19d26b1 100644 --- a/spec/routing/routing_spec.rb +++ b/spec/routing/routing_spec.rb @@ -128,7 +128,7 @@ end # profile_update PUT /profile/update(.:format) profile#update describe ProfilesController, "routing" do it "to #account" do - get("/profile/account").should route_to('profiles#account') + get("/profile/account").should route_to('profiles/accounts#show') end it "to #history" do diff --git a/spec/seed_project.tar.gz b/spec/seed_project.tar.gz Binary files differindex 3ffafed18d0..7abb51ebdfd 100644 --- a/spec/seed_project.tar.gz +++ b/spec/seed_project.tar.gz diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index e3c25fa0469..2870f59195a 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -26,6 +26,7 @@ describe GitPushService do it { should include(ref: @ref) } it { should include(user_id: user.id) } it { should include(user_name: user.name) } + it { should include(project_id: project.id) } context "with repository data" do subject { @push_data[:repository] } |
