diff options
author | Douwe Maan <douwe@gitlab.com> | 2015-10-21 08:39:19 +0200 |
---|---|---|
committer | Douwe Maan <douwe@gitlab.com> | 2015-10-21 08:39:19 +0200 |
commit | d222ce8b348ff2f6c76c3f1b05f8a4e91efcf0b4 (patch) | |
tree | 9b4fa2659335c681b41d1f5c6d2fe329d52bf1e1 | |
parent | 7ae139566b46050b31bf01a6302af560fc129819 (diff) | |
parent | 249a9476d44e89bf1ab12fbc40bf81478faafd12 (diff) | |
download | gitlab-ce-d222ce8b348ff2f6c76c3f1b05f8a4e91efcf0b4.tar.gz |
Merge branch 'master' into full-width-tables
68 files changed, 715 insertions, 225 deletions
diff --git a/CHANGELOG b/CHANGELOG index f8daa6d246c..e956d074d72 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,11 +1,18 @@ Please view this file on the master branch, on stable branches it's out of date. v 8.2.0 (unreleased) + - Improved performance of replacing references in comments + - Fix duplicate repositories in GitHub import page (Stan Hu) + - Redirect to a default path if HTTP_REFERER is not set (Stan Hu) - Show last project commit to default branch on project home page - Highlight comment based on anchor in URL - Adds ability to remove the forked relationship from project settings screen. (Han Loong Liauw) + - Improved performance of sorting milestone issues + - Allow users to select the Files view as default project view (Cristian Bica) v 8.1.0 (unreleased) + - Send an email to admin email when a user is reported for spam (Jonathan Rochkind) + - Show notifications button when user is member of group rather than project (Grzegorz Bizon) - Fix bug preventing mentioned issued from being closed when MR is merged using fast-forward merge. - Fix nonatomic database update potentially causing project star counts to go negative (Stan Hu) - Fix error preventing displaying of commit data for a directory with a leading dot (Stan Hu) @@ -72,8 +79,13 @@ v 8.1.0 (unreleased) - Only render 404 page from /public - Hide passwords from services API (Alex Lossent) - Fix: Images cannot show when projects' path was changed + - Optimize query when filtering on issuables (Zeger-Jan van de Weg) - Fix padding of outdated discussion item. +v 8.0.5 + - Correct lookup-by-email for LDAP logins + - Fix loading spinner sometimes not being hidden on Merge Request tab switches + v 8.0.4 - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu) - Fix referrals for :back and relative URL installs diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 32d219d4d60..5949a0fd5ad 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -65,3 +65,48 @@ line-height: 42px; } } + +.cover-block { + text-align: center; + background: #f7f8fa; + margin: -$gl-padding; + margin-bottom: 0; + padding: 44px $gl-padding; + border-bottom: 1px solid $border-color; + position: relative; + + .avatar-holder { + margin-bottom: 16px; + + .avatar, .identicon { + margin: 0 auto; + float: none; + } + + .identicon { + @include border-radius(50%); + } + } + + .cover-title { + color: $gl-header-color; + margin: 0; + font-size: 23px; + font-weight: normal; + margin: 16px 0 5px 0; + color: #4c4e54; + font-size: 23px; + line-height: 1.1; + } + + .cover-desc { + padding: 0 $gl-padding; + color: $gl-text-color; + } + + .cover-controls { + position: absolute; + top: 10px; + right: 10px; + } +} diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 1857c1659aa..e6d1cca9f7a 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -18,7 +18,6 @@ font-family: $monospace_font; white-space: pre; word-wrap: normal; - padding: 1px 2px; } kbd { diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 8e4f0eb2b25..b7391e5303b 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -47,3 +47,9 @@ } } } + +.calendar-hint { + margin-top: -12px; + float: right; + font-size: 12px; +} diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb index 65dbd5ef551..2f4054eaa11 100644 --- a/app/controllers/abuse_reports_controller.rb +++ b/app/controllers/abuse_reports_controller.rb @@ -9,6 +9,10 @@ class AbuseReportsController < ApplicationController @abuse_report.reporter = current_user if @abuse_report.save + if current_application_settings.admin_notification_email.present? + AbuseReportMailer.delay.notify(@abuse_report.id) + end + message = "Thank you for your report. A GitLab administrator will look into it shortly." redirect_to root_path, notice: message else diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 7c134d2ec9b..039f18f23e0 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -55,6 +55,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :default_snippet_visibility, :restricted_signup_domains_raw, :version_check_enabled, + :admin_notification_email, :user_oauth_applications, restricted_visibility_levels: [], import_sources: [] diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb index 0808024fc39..497c34f8f49 100644 --- a/app/controllers/admin/broadcast_messages_controller.rb +++ b/app/controllers/admin/broadcast_messages_controller.rb @@ -19,7 +19,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController BroadcastMessage.find(params[:id]).destroy respond_to do |format| - format.html { redirect_to :back } + format.html { redirect_back_or_default(default: { action: 'index' }) } format.js { render nothing: true } end end diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb index d670386f8c6..0bd19c49d8f 100644 --- a/app/controllers/admin/hooks_controller.rb +++ b/app/controllers/admin/hooks_controller.rb @@ -35,7 +35,7 @@ class Admin::HooksController < Admin::ApplicationController } @hook.execute(data, 'system_hooks') - redirect_to :back + redirect_back_or_default end def hook_params diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 00f41a10dd1..c63d0793e31 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -33,33 +33,33 @@ class Admin::UsersController < Admin::ApplicationController def block if user.block - redirect_to :back, notice: "Successfully blocked" + redirect_back_or_admin_user(notice: "Successfully blocked") else - redirect_to :back, alert: "Error occurred. User was not blocked" + redirect_back_or_admin_user(alert: "Error occurred. User was not blocked") end end def unblock if user.activate - redirect_to :back, notice: "Successfully unblocked" + redirect_back_or_admin_user(notice: "Successfully unblocked") else - redirect_to :back, alert: "Error occurred. User was not unblocked" + redirect_back_or_admin_user(alert: "Error occurred. User was not unblocked") end end def unlock if user.unlock_access! - redirect_to :back, alert: "Successfully unlocked" + redirect_back_or_admin_user(alert: "Successfully unlocked") else - redirect_to :back, alert: "Error occurred. User was not unlocked" + redirect_back_or_admin_user(alert: "Error occurred. User was not unlocked") end end def confirm if user.confirm - redirect_to :back, notice: "Successfully confirmed" + redirect_back_or_admin_user(notice: "Successfully confirmed") else - redirect_to :back, alert: "Error occurred. User was not confirmed" + redirect_back_or_admin_user(alert: "Error occurred. User was not confirmed") end end @@ -138,7 +138,7 @@ class Admin::UsersController < Admin::ApplicationController user.update_secondary_emails! respond_to do |format| - format.html { redirect_to :back, notice: "Successfully removed email." } + format.html { redirect_back_or_admin_user(notice: "Successfully removed email.") } format.js { render nothing: true } end end @@ -157,4 +157,12 @@ class Admin::UsersController < Admin::ApplicationController :projects_limit, :can_create_group, :admin, :key_id ) end + + def redirect_back_or_admin_user(options = {}) + redirect_back_or_default(default: default_route, options: options) + end + + def default_route + [:admin, @user] + end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f0124c6bd60..865deb7d46a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -33,6 +33,10 @@ class ApplicationController < ActionController::Base render_404 end + def redirect_back_or_default(default: root_path, options: {}) + redirect_to request.referer.present? ? :back : default, options + end + protected # From https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb index aae77d384c6..67bf4190e7e 100644 --- a/app/controllers/import/github_controller.rb +++ b/app/controllers/import/github_controller.rb @@ -11,10 +11,6 @@ class Import::GithubController < Import::BaseController def status @repos = client.repos - client.orgs.each do |org| - @repos += client.org_repos(org.login) - end - @already_added_projects = current_user.created_projects.where(import_type: "github") already_added_projects_names = @already_added_projects.pluck(:import_source) diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb index 41472a6fe6c..e0de31f2251 100644 --- a/app/controllers/import/google_code_controller.rb +++ b/app/controllers/import/google_code_controller.rb @@ -10,18 +10,18 @@ class Import::GoogleCodeController < Import::BaseController dump_file = params[:dump_file] unless dump_file.respond_to?(:read) - return redirect_to :back, alert: "You need to upload a Google Takeout archive." + return redirect_back_or_default(options: { alert: "You need to upload a Google Takeout archive." }) end begin dump = JSON.parse(dump_file.read) rescue - return redirect_to :back, alert: "The uploaded file is not a valid Google Takeout archive." + return redirect_back_or_default(options: { alert: "The uploaded file is not a valid Google Takeout archive." }) end client = Gitlab::GoogleCodeImport::Client.new(dump) unless client.valid? - return redirect_to :back, alert: "The uploaded file is not a valid Google Takeout archive." + return redirect_back_or_default(options: { alert: "The uploaded file is not a valid Google Takeout archive." }) end session[:google_code_dump] = dump diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index 8ef10a17f55..94bb108c5f5 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -14,7 +14,7 @@ class InvitesController < ApplicationController redirect_to path, notice: "You have been granted #{member.human_access} access to #{label}." else - redirect_to :back, alert: "The invitation could not be accepted." + redirect_back_or_default(options: { alert: "The invitation could not be accepted." }) end end @@ -31,7 +31,7 @@ class InvitesController < ApplicationController redirect_to path, notice: "You have declined the invitation to join #{label}." else - redirect_to :back, alert: "The invitation could not be declined." + redirect_back_or_default(options: { alert: "The invitation could not be declined." }) end end diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb index 22423651c17..1fd1d6882df 100644 --- a/app/controllers/profiles/notifications_controller.rb +++ b/app/controllers/profiles/notifications_controller.rb @@ -29,7 +29,7 @@ class Profiles::NotificationsController < Profiles::ApplicationController flash[:alert] = "Failed to save new settings" end - redirect_to :back + redirect_back_or_default(default: profile_notifications_path) end format.js diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 26a4de15462..8da7b4d50ea 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -26,7 +26,7 @@ class ProfilesController < Profiles::ApplicationController end respond_to do |format| - format.html { redirect_to :back } + format.html { redirect_back_or_default(default: { action: 'show' }) } end end diff --git a/app/controllers/projects/ci_services_controller.rb b/app/controllers/projects/ci_services_controller.rb index 6d2756eba3d..406f313ae79 100644 --- a/app/controllers/projects/ci_services_controller.rb +++ b/app/controllers/projects/ci_services_controller.rb @@ -30,7 +30,7 @@ class Projects::CiServicesController < Projects::ApplicationController message = { alert: 'We tried to test the service but error occurred' } end - redirect_to :back, message + redirect_back_or_default(options: message) end private diff --git a/app/controllers/projects/ci_web_hooks_controller.rb b/app/controllers/projects/ci_web_hooks_controller.rb index 7f40ddcb3f3..a2d470d4a69 100644 --- a/app/controllers/projects/ci_web_hooks_controller.rb +++ b/app/controllers/projects/ci_web_hooks_controller.rb @@ -24,7 +24,7 @@ class Projects::CiWebHooksController < Projects::ApplicationController def test Ci::TestHookService.new.execute(hook, current_user) - redirect_to :back + redirect_back_or_default(default: { action: 'index' }) end def destroy diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index d15004f93a6..71aaad1fad6 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -17,9 +17,10 @@ class Projects::CompareController < Projects::ApplicationController execute(@project, head_ref, @project, base_ref) if compare_result - @commits = compare_result.commits + @commits = Commit.decorate(compare_result.commits, @project) @diffs = compare_result.diffs @commit = @commits.last + @first_commit = @commits.first @line_notes = [] end end diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb index 40e2b37912b..7d09288bc80 100644 --- a/app/controllers/projects/deploy_keys_controller.rb +++ b/app/controllers/projects/deploy_keys_controller.rb @@ -46,7 +46,7 @@ class Projects::DeployKeysController < Projects::ApplicationController def disable @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]).destroy - redirect_to :back + redirect_back_or_default(default: { action: 'index' }) end protected diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 4e5b4125f5a..c7569541899 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -37,7 +37,7 @@ class Projects::HooksController < Projects::ApplicationController flash[:alert] = 'Hook execution failed. Ensure the project has commits.' end - redirect_to :back + redirect_back_or_default(default: { action: 'index' }) end def destroy diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index cc8321d97ad..e767efbdc0c 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -106,7 +106,7 @@ class Projects::IssuesController < Projects::ApplicationController def bulk_update result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute - redirect_to :back, notice: "#{result[:count]} issues updated" + redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" }) end def toggle_subscription diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 0d9c5461959..16c42386623 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -56,6 +56,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController def diffs @commit = @merge_request.last_commit + @first_commit = @merge_request.first_commit + @comments_allowed = @reply_allowed = true @comments_target = { noteable_type: 'MergeRequest', @@ -89,7 +91,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController @target_project = merge_request.target_project @source_project = merge_request.source_project @commits = @merge_request.compare_commits - @commit = @merge_request.compare_commits.last + @commit = @merge_request.last_commit + @first_commit = @merge_request.first_commit @diffs = @merge_request.compare_diffs @note_counts = Note.where(commit_id: @commits.map(&:id)). group(:commit_id).count diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 86f4a02a6e9..15506bd677a 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -75,11 +75,7 @@ class Projects::MilestonesController < Projects::ApplicationController end def sort_issues - @issues = @milestone.issues.where(id: params['sortable_issue']) - @issues.each do |issue| - issue.position = params['sortable_issue'].index(issue.id.to_s) + 1 - issue.save - end + @milestone.sort_issues(params['sortable_issue'].map(&:to_i)) render json: { saved: true } end diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 0f5d82ce133..41cd08c93c6 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -25,7 +25,7 @@ class Projects::NotesController < Projects::ApplicationController respond_to do |format| format.json { render_note_json(@note) } - format.html { redirect_to :back } + format.html { redirect_back_or_default } end end @@ -34,7 +34,7 @@ class Projects::NotesController < Projects::ApplicationController respond_to do |format| format.json { render_note_json(@note) } - format.html { redirect_to :back } + format.html { redirect_back_or_default } end end diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index cf73bc01c8f..9de5269cd25 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -72,7 +72,8 @@ class Projects::ProjectMembersController < Projects::ApplicationController def leave if @project.namespace == current_user.namespace - return redirect_to(:back, alert: 'You can not leave your own project. Transfer or delete the project.') + message = 'You can not leave your own project. Transfer or delete the project.' + return redirect_back_or_default(default: { action: 'index' }, options: { alert: message }) end @project.project_members.find_by(user_id: current_user).destroy diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 129068ef019..42dbb497e01 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -12,7 +12,7 @@ class Projects::ServicesController < Projects::ApplicationController # Parameters to ignore if no value is specified FILTER_BLANK_PARAMS = [:password] - + # Authorize before_action :authorize_admin_project! before_action :service, only: [:edit, :update, :test] @@ -52,7 +52,7 @@ class Projects::ServicesController < Projects::ApplicationController message = { alert: error_message } end - redirect_to :back, message + redirect_back_or_default(options: message) end private diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 73200396ecc..82119022cf9 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -101,7 +101,7 @@ class ProjectsController < ApplicationController render 'projects/empty' else if current_user - @membership = @project.project_member_by_id(current_user.id) + @membership = @project.team.find_member(current_user.id) end render :show @@ -246,6 +246,8 @@ class ProjectsController < ApplicationController project.repository_exists? && !project.empty_repo? end + # Override get_id from ExtractsPath, which returns the branch and file path + # for the blob/tree, which in this case is just the root of the default branch. def get_id project.repository.root_ref end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 97c7e74c294..c407dfc163a 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -53,15 +53,36 @@ class IssuableFinder end end + def project? + params[:project_id].present? + end + def project return @project if defined?(@project) - @project = - if params[:project_id].present? - Project.find(params[:project_id]) - else - nil - end + if project? + @project = Project.find(params[:project_id]) + + unless Ability.abilities.allowed?(current_user, :read_project, @project) + @project = nil + end + else + @project = nil + end + + @project + end + + def projects + return @projects if defined?(@projects) + + if project? + project + elsif current_user && params[:authorized_only].presence && !current_user_related? + current_user.authorized_projects + else + ProjectsFinder.new.execute(current_user) + end end def search @@ -72,7 +93,7 @@ class IssuableFinder params[:milestone_title].present? end - def no_milestones? + def filter_by_no_milestone? milestones? && params[:milestone_title] == Milestone::None.title end @@ -81,12 +102,22 @@ class IssuableFinder @milestones = if milestones? - Milestone.where(title: params[:milestone_title]) + scope = Milestone.where(project_id: projects) + + scope.where(title: params[:milestone_title]) else nil end end + def labels? + params[:label_name].present? + end + + def filter_by_no_label? + labels? && params[:label_name] == Label::None.title + end + def assignee? params[:assignee_id].present? end @@ -120,19 +151,7 @@ class IssuableFinder private def init_collection - table_name = klass.table_name - - if project - if Ability.abilities.allowed?(current_user, :read_project, project) - project.send(table_name) - else - [] - end - elsif current_user && params[:authorized_only].presence && !current_user_related? - klass.of_projects(current_user.authorized_projects).references(:project) - else - klass.of_projects(ProjectsFinder.new.execute(current_user)).references(:project) - end + klass.all end def by_scope(items) @@ -170,7 +189,12 @@ class IssuableFinder end def by_project(items) - items = items.of_projects(project.id) if project + items = + if projects + items.of_projects(projects).references(:project) + else + items.none + end items end @@ -185,18 +209,6 @@ class IssuableFinder items.sort(params[:sort]) end - def by_milestone(items) - if milestones? - if no_milestones? - items = items.where(milestone_id: [-1, nil]) - else - items = items.where(milestone_id: milestones.try(:pluck, :id)) - end - end - - items - end - def by_assignee(items) if assignee? items = items.where(assignee_id: assignee.try(:id)) @@ -213,20 +225,36 @@ class IssuableFinder items end - def by_label(items) - if params[:label_name].present? - if params[:label_name] == Label::None.title - item_ids = LabelLink.where(target_type: klass.name).pluck(:target_id) + def by_milestone(items) + if milestones? + if filter_by_no_milestone? + items = items.where(milestone_id: [-1, nil]) + else + items = items.joins(:milestone).where(milestones: { title: params[:milestone_title] }) + + if projects + items = items.where(milestones: { project_id: projects }) + end + end + end + + items + end - items = items.where('id NOT IN (?)', item_ids) + def by_label(items) + if labels? + if filter_by_no_label? + items = items. + joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{klass.name}' AND label_links.target_id = #{klass.table_name}.id"). + where(label_links: { id: nil }) else label_names = params[:label_name].split(",") - item_ids = LabelLink.joins(:label). - where('labels.title in (?)', label_names). - where(target_type: klass.name).pluck(:target_id) + items = items.joins(:labels).where(labels: { title: label_names }) - items = items.where(id: item_ids) + if projects + items = items.where(labels: { project_id: projects }) + end end end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index b896fba3704..e65e37211c4 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -170,7 +170,8 @@ module DiffHelper def commit_for_diff(diff) if diff.deleted_file - @merge_request ? @merge_request.commits.last : @commit.parents.first + first_commit = @first_commit || @commit + first_commit.parent else @commit end diff --git a/app/mailers/abuse_report_mailer.rb b/app/mailers/abuse_report_mailer.rb new file mode 100644 index 00000000000..f0c41f69a5c --- /dev/null +++ b/app/mailers/abuse_report_mailer.rb @@ -0,0 +1,12 @@ +class AbuseReportMailer < BaseMailer + include Gitlab::CurrentSettings + + def notify(abuse_report_id) + @abuse_report = AbuseReport.find(abuse_report_id) + + mail( + to: current_application_settings.admin_notification_email, + subject: "#{@abuse_report.user.name} (#{@abuse_report.user.username}) was reported for abuse" + ) + end +end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index c8841178e93..05430c2ee18 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -44,6 +44,10 @@ class ApplicationSetting < ActiveRecord::Base allow_blank: true, format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" } + validates :admin_notification_email, + allow_blank: true, + email: true + validates_each :restricted_visibility_levels do |record, attr, value| unless value.nil? value.each do |level| diff --git a/app/models/commit.rb b/app/models/commit.rb index 23b5e38336c..492f6be1ce3 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -164,6 +164,14 @@ class Commit @committer ||= User.find_by_any_email(committer_email) end + def parents + @parents ||= parent_ids.map { |id| project.commit(id) } + end + + def parent + @parent ||= project.commit(self.parent_id) if self.parent_id + end + def notes project.notes.for_commit_id(self.id) end @@ -181,10 +189,6 @@ class Commit @raw.short_id(7) end - def parents - @parents ||= Commit.decorate(super, project) - end - def ci_commit project.ci_commit(sha) end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 0042b95c4f1..21861a46a84 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -40,7 +40,7 @@ class MergeRequest < ActiveRecord::Base after_create :create_merge_request_diff after_update :update_merge_request_diff - delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil + delegate :commits, :diffs, to: :merge_request_diff, prefix: nil # When this attribute is true some MR validation is ignored # It allows us to close or modify broken merge requests @@ -157,6 +157,18 @@ class MergeRequest < ActiveRecord::Base reference end + def last_commit + merge_request_diff ? merge_request_diff.last_commit : compare_commits.last + end + + def first_commit + merge_request_diff ? merge_request_diff.first_commit : compare_commits.first + end + + def last_commit_short_sha + last_commit.short_id + end + def validate_branches if target_project == source_project && target_branch == source_branch errors.add :branch_conflict, "You can not use same project/branch for source and target" diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index bc2d691ece0..6575d0bc81f 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -55,6 +55,10 @@ class MergeRequestDiff < ActiveRecord::Base commits.first end + def first_commit + commits.last + end + def last_commit_short_sha @last_commit_short_sha ||= last_commit.short_id end diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 84acba30b6b..2ff16e2825c 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -105,4 +105,36 @@ class Milestone < ActiveRecord::Base def author_id nil end + + # Sorts the issues for the given IDs. + # + # This method runs a single SQL query using a CASE statement to update the + # position of all issues in the current milestone (scoped to the list of IDs). + # + # Given the ids [10, 20, 30] this method produces a SQL query something like + # the following: + # + # UPDATE issues + # SET position = CASE + # WHEN id = 10 THEN 1 + # WHEN id = 20 THEN 2 + # WHEN id = 30 THEN 3 + # ELSE position + # END + # WHERE id IN (10, 20, 30); + # + # This method expects that the IDs given in `ids` are already Fixnums. + def sort_issues(ids) + pairs = [] + + ids.each_with_index do |id, index| + pairs << id + pairs << index + 1 + end + + conditions = 'WHEN id = ? THEN ? ' * ids.length + + issues.where(id: ids). + update_all(["position = CASE #{conditions} ELSE position END", *pairs]) + end end diff --git a/app/models/repository.rb b/app/models/repository.rb index 921e1a9e426..e2d4f74407f 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -312,13 +312,7 @@ class Repository end def blob_for_diff(commit, diff) - file = blob_at(commit.id, diff.new_path) - - unless file - file = prev_blob_for_diff(commit, diff) - end - - file + blob_at(commit.id, diff.file_path) end def prev_blob_for_diff(commit, diff) diff --git a/app/views/abuse_report_mailer/notify.html.haml b/app/views/abuse_report_mailer/notify.html.haml new file mode 100644 index 00000000000..619533e09a7 --- /dev/null +++ b/app/views/abuse_report_mailer/notify.html.haml @@ -0,0 +1,11 @@ +%p + #{link_to @abuse_report.user.name, user_url(@abuse_report.user)} + (@#{@abuse_report.user.username}) was reported for abuse by + #{link_to @abuse_report.reporter.name, user_url(@abuse_report.reporter)} + (@#{@abuse_report.reporter.username}). + +%blockquote + = @abuse_report.message + +%p + = link_to "View details", abuse_reports_url diff --git a/app/views/abuse_report_mailer/notify.text.haml b/app/views/abuse_report_mailer/notify.text.haml new file mode 100644 index 00000000000..7dacf857035 --- /dev/null +++ b/app/views/abuse_report_mailer/notify.text.haml @@ -0,0 +1,5 @@ +#{@abuse_report.user.name} (@#{@abuse_report.user.username}) was reported for abuse by #{@abuse_report.reporter.name} (@#{@abuse_report.reporter.username}). +\ +> #{@abuse_report.message} +\ +View details: #{admin_abuse_reports_url} diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index a36ae0b766c..7a78526e09a 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -47,6 +47,12 @@ = f.label :version_check_enabled do = f.check_box :version_check_enabled Version check enabled + .form-group + = f.label :admin_notification_email, class: 'control-label col-sm-2' + .col-sm-10 + = f.text_field :admin_notification_email, class: 'form-control' + .help-block + Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area. %fieldset %legend Account and Limit Settings diff --git a/app/views/users/_profile.html.haml b/app/views/admin/users/_profile.html.haml index 90d9980c85c..90d9980c85c 100644 --- a/app/views/users/_profile.html.haml +++ b/app/views/admin/users/_profile.html.haml diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 231bcb0426f..0848504b7a6 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -14,7 +14,7 @@ %strong = link_to user_path(@user) do = @user.username - = render 'users/profile', user: @user + = render 'admin/users/profile', user: @user .panel.panel-default .panel-heading diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index 2f7d7e86f56..854cda57c39 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -41,4 +41,8 @@ #{link_to "view it on GitLab", @target_url}. - else #{link_to "View it on GitLab", @target_url} + %br + You're receiving this email because of your account on #{link_to Gitlab.config.gitlab.host, root_url}. + If you'd like to receive fewer emails, you can adjust your notification settings. + = email_action @target_url diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index 3bc2daeec4e..0c298844912 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -1,14 +1,20 @@ -- return unless @membership +- case @membership +- when ProjectMember + = form_tag profile_notifications_path, method: :put, remote: true, class: 'inline', id: 'notification-form' do + = hidden_field_tag :notification_type, 'project' + = hidden_field_tag :notification_id, @membership.id + = hidden_field_tag :notification_level + %span.dropdown + %a.dropdown-new.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"} + = icon('bell') + = notification_label(@membership) + = icon('angle-down') + %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown + - Notification.project_notification_levels.each do |level| + = notification_list_item(level, @membership) -= form_tag profile_notifications_path, method: :put, remote: true, class: 'inline', id: 'notification-form' do - = hidden_field_tag :notification_type, 'project' - = hidden_field_tag :notification_id, @membership.id - = hidden_field_tag :notification_level - %span.dropdown - %a.dropdown-new.btn.btn-new#notifications-button{href: '#', "data-toggle" => "dropdown"} - = icon('bell') - = notification_label(@membership) - = icon('angle-down') - %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown - - Notification.project_notification_levels.each do |level| - = notification_list_item(level, @membership) +- when GroupMember + .btn.btn-new.disabled.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."} + = icon('bell') + = notification_label(@membership) + = icon('angle-down') diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 4f1965bfb39..56b51f038ba 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -15,8 +15,8 @@ .files - diff_files.each_with_index do |diff_file, index| - - diff_commit = commit_for_diff(diff_file.diff) - - blob = project.repository.blob_for_diff(diff_commit, diff_file.diff) + - diff_commit = commit_for_diff(diff_file) + - blob = project.repository.blob_for_diff(diff_commit, diff_file) - next unless blob = render 'projects/diffs/file', i: index, project: project, diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 9698921f6da..410ff6abb43 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -1,5 +1,5 @@ .diff-file{id: "diff-#{i}", data: diff_file_html_data(project, diff_commit, diff_file)} - .diff-header{id: "file-path-#{hexdigest(diff_file.new_path || diff_file.old_path)}"} + .diff-header{id: "file-path-#{hexdigest(diff_file.file_path)}"} - if diff_file.diff.submodule? %span - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path) @@ -38,7 +38,7 @@ - else = render "projects/diffs/text_file", diff_file: diff_file, index: i - elsif blob.image? - - old_file = project.repository.prev_blob_for_diff(@commit, diff_file) + - old_file = project.repository.prev_blob_for_diff(diff_commit, diff_file) = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i - else .nothing-here-block No preview for this file type diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index 68dda1424cf..10efb811939 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -1,44 +1,43 @@ -- if @merge_request.has_ci? - - ci_commit = @merge_request.source_project.ci_commit(@merge_request.source_sha) - - if ci_commit - - status = ci_commit.status - .mr-widget-heading - .ci_widget{class: "ci-#{status}"} - = ci_status_icon(ci_commit) +- ci_commit = @merge_request.source_project.ci_commit(@merge_request.source_sha) +- if ci_commit + - status = ci_commit.status + .mr-widget-heading + .ci_widget{class: "ci-#{status}"} + = ci_status_icon(ci_commit) + %span CI build #{status} + for #{@merge_request.last_commit_short_sha}. + %span.ci-coverage + = link_to "View build details", ci_status_path(ci_commit) + +- elsif @merge_request.has_ci? + - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX + - # Remove in later versions when services like Jenkins will set CI status via Commit status API + .mr-widget-heading + - [:success, :skipped, :canceled, :failed, :running, :pending].each do |status| + .ci_widget{class: "ci-#{status}", style: "display:none"} + - if status == :success + - status = "passed" + = icon("check-circle") + - else + = icon("circle") %span CI build #{status} for #{@merge_request.last_commit_short_sha}. %span.ci-coverage - = link_to "View build details", ci_status_path(ci_commit) - - - else - - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX - - # Remove in later versions when services like Jenkins will set CI status via Commit status API - .mr-widget-heading - - [:success, :skipped, :canceled, :failed, :running, :pending].each do |status| - .ci_widget{class: "ci-#{status}", style: "display:none"} - - if status == :success - - status = "passed" - = icon("check-circle") - - else - = icon("circle") - %span CI build #{status} - for #{@merge_request.last_commit_short_sha}. - %span.ci-coverage - - if ci_build_details_path(@merge_request) - = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" + - if ci_build_details_path(@merge_request) + = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink" - .ci_widget - = icon("spinner spin") - Checking CI status for #{@merge_request.last_commit_short_sha}… + .ci_widget + = icon("spinner spin") + Checking CI status for #{@merge_request.last_commit_short_sha}… - .ci_widget.ci-not_found{style: "display:none"} - = icon("times-circle") - Could not find CI status for #{@merge_request.last_commit_short_sha}. + .ci_widget.ci-not_found{style: "display:none"} + = icon("times-circle") + Could not find CI status for #{@merge_request.last_commit_short_sha}. - .ci_widget.ci-error{style: "display:none"} - = icon("times-circle") - Could not connect to the CI server. Please check your settings and try again. + .ci_widget.ci-error{style: "display:none"} + = icon("times-circle") + Could not connect to the CI server. Please check your settings and try again. - :coffeescript - $ -> - merge_request_widget.getCiStatus() + :coffeescript + $ -> + merge_request_widget.getCiStatus() diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index b23b2f0d5eb..2e4aab36301 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -6,7 +6,7 @@ type: 'button', | class: "btn #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", | :"data-clone" => project.ssh_url_to_repo, | - :"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH", + :"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH.", :"data-html" => "true", :"data-container" => "body"} SSH @@ -15,7 +15,7 @@ type: 'button', | class: "btn #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", | :"data-clone" => project.http_url_to_repo, | - :"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}", + :"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}.", :"data-html" => "true", :"data-container" => "body"} = gitlab_config.protocol.upcase diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml index 922b0c6cebf..7f29918dba3 100644 --- a/app/views/users/calendar.html.haml +++ b/app/views/users/calendar.html.haml @@ -1,7 +1,3 @@ -%h4 - Contributions calendar - .pull-right - %small Issues, merge requests and push events #cal-heatmap.calendar :javascript new Calendar( @@ -10,3 +6,5 @@ #{@starting_month}, '#{user_calendar_activities_path}' ); + +.calendar-hint Summary of issues, merge requests and push events diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 2a64708d07c..4ea4a1f92c2 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -6,47 +6,72 @@ = render 'shared/show_aside' -.row - %section.col-md-7 - .header-with-avatar - = link_to avatar_icon(@user, 400), target: '_blank' do - = image_tag avatar_icon(@user, 90), class: "avatar avatar-tile s90", alt: '' - %h3 - = @user.name - - if @user == current_user - .pull-right.hidden-xs - = link_to profile_path, class: 'btn btn-sm' do - = icon('user') - Profile settings - - elsif current_user - .report_abuse.pull-right - - if @user.abuse_report - %span#report_abuse_btn.light.btn.btn-sm.btn-close{title: 'Already reported for abuse', data: {toggle: 'tooltip', placement: 'right', container: 'body'}} - = icon('exclamation-circle') - - else - %a.light.btn.btn-sm{href: new_abuse_report_path(user_id: @user.id), title: 'Report abuse', data: {toggle: 'tooltip', placement: 'right', container: 'body'}} - = icon('exclamation-circle') +.cover-block + .avatar-holder + = link_to avatar_icon(@user, 400), target: '_blank' do + = image_tag avatar_icon(@user, 90), class: "avatar s90", alt: '' + .cover-title + = @user.name + + .cover-desc + %span + @#{@user.username}. + - if @user.bio.present? + %span + #{@user.bio}. + %span + Member since #{@user.created_at.stamp("Aug 21, 2011")} + + .cover-desc + - unless @user.public_email.blank? + = link_to @user.public_email, "mailto:#{@user.public_email}" + - unless @user.skype.blank? + · + = link_to "Skype", "skype:#{@user.skype}" + - unless @user.linkedin.blank? + · + = link_to "LinkedIn", "http://www.linkedin.com/in/#{@user.linkedin}" + - unless @user.twitter.blank? + · + = link_to "Twitter", "http://www.twitter.com/#{@user.twitter}" + - unless @user.website_url.blank? + · + = link_to @user.short_website_url, @user.full_website_url + - unless @user.location.blank? + · + = @user.location - .username - @#{@user.username} - .description - - if @user.bio.present? - = @user.bio - .clearfix + .cover-controls + - if @user == current_user + = link_to profile_path, class: 'btn btn-gray' do + = icon('pencil') + - elsif current_user + .report-abuse + - if @user.abuse_report + %button.btn.btn-danger{ title: 'Already reported for abuse', + data: { toggle: 'tooltip', placement: 'left', container: 'body' }} + = icon('exclamation-circle') + - else + = link_to new_abuse_report_path(user_id: @user.id), class: 'btn btn-gray', + title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do + = icon('exclamation-circle') +.gray-content-block.second-block + .user-calendar + %h4.center.light + %i.fa.fa-spinner.fa-spin + .user-calendar-activities + + +.row.prepend-top-20 + %section.col-md-7 - if @groups.any? .prepend-top-20 %h4 Groups = render 'groups', groups: @groups %hr - .hidden-xs - .user-calendar - %h4.center.light - %i.fa.fa-spinner.fa-spin - .user-calendar-activities - %hr %h4 User Activity @@ -59,7 +84,6 @@ .content_list = spinner %aside.col-md-5 - = render 'profile', user: @user = render 'projects', projects: @projects, contributed_projects: @contributed_projects :coffeescript diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb index 378354efd5a..03da29c4c68 100644 --- a/db/fixtures/development/05_users.rb +++ b/db/fixtures/development/05_users.rb @@ -1,5 +1,5 @@ Gitlab::Seeder.quiet do - (2..20).each do |i| + 20.times do |i| begin User.create!( username: FFaker::Internet.user_name, @@ -15,7 +15,7 @@ Gitlab::Seeder.quiet do end end - (1..5).each do |i| + 5.times do |i| begin User.create!( username: "user#{i}", diff --git a/db/fixtures/development/07_milestones.rb b/db/fixtures/development/07_milestones.rb index a43116829d9..e028ac82ba3 100644 --- a/db/fixtures/development/07_milestones.rb +++ b/db/fixtures/development/07_milestones.rb @@ -1,6 +1,6 @@ Gitlab::Seeder.quiet do Project.all.each do |project| - (1..5).each do |i| + 5.times do |i| milestone_params = { title: "v#{i}.0", description: FFaker::Lorem.sentence, diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb index c636e96381c..4fa572fca9b 100644 --- a/db/fixtures/development/09_issues.rb +++ b/db/fixtures/development/09_issues.rb @@ -1,6 +1,6 @@ Gitlab::Seeder.quiet do Project.all.each do |project| - (1..10).each do |i| + 10.times do issue_params = { title: FFaker::Lorem.sentence(6), description: FFaker::Lorem.sentence, diff --git a/db/fixtures/development/12_snippets.rb b/db/fixtures/development/12_snippets.rb index 3bd4b442ade..74898544a69 100644 --- a/db/fixtures/development/12_snippets.rb +++ b/db/fixtures/development/12_snippets.rb @@ -22,7 +22,7 @@ class Member < ActiveRecord::Base end eos - (1..50).each do |i| + 50.times do |i| user = User.all.sample PersonalSnippet.seed(:id, [{ diff --git a/db/migrate/20151008143519_add_admin_notification_email_setting.rb b/db/migrate/20151008143519_add_admin_notification_email_setting.rb new file mode 100644 index 00000000000..0bb581efe2c --- /dev/null +++ b/db/migrate/20151008143519_add_admin_notification_email_setting.rb @@ -0,0 +1,5 @@ +class AddAdminNotificationEmailSetting < ActiveRecord::Migration + def change + add_column :application_settings, :admin_notification_email, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 886b05f3e56..b05fa708775 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -46,6 +46,7 @@ ActiveRecord::Schema.define(version: 20151016195706) do t.integer "session_expire_delay", default: 10080, null: false t.text "import_sources" t.text "help_page_text" + t.string "admin_notification_email" end create_table "audit_events", force: true do |t| diff --git a/doc/update/8.0-to-8.1.md b/doc/update/8.0-to-8.1.md index 4dacc97f7f7..d57c0d0674d 100644 --- a/doc/update/8.0-to-8.1.md +++ b/doc/update/8.0-to-8.1.md @@ -70,7 +70,16 @@ sudo -u git -H git fetch sudo -u git -H git checkout v2.6.5 ``` -### 5. Install libs, migrations, etc. +### 5. Update gitlab-git-http-server + +```bash +cd /home/git/gitlab-git-http-server +sudo -u git -H git fetch origin +sudo -u git -H git checkout 0.3.0 +sudo -u git -H make +``` + +### 6. Install libs, migrations, etc. ```bash cd /home/git/gitlab @@ -91,7 +100,7 @@ sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab ``` -### 6. Update configuration files +### 7. Update configuration files #### New configuration options for `gitlab.yml` @@ -101,12 +110,33 @@ There are new configuration options available for [`gitlab.yml`](config/gitlab.y git diff origin/8-0-stable:config/gitlab.yml.example origin/8-1-stable:config/gitlab.yml.example ``` -### 7. Start application +#### Nginx configuration + +View changes between the previous recommended Nginx configuration and the +current one: + +```sh +# For HTTPS configurations +git diff origin/8-0-stable:lib/support/nginx/gitlab-ssl origin/8-1-stable:lib/support/nginx/gitlab-ssl + +# For HTTP configurations +git diff origin/8-0-stable:lib/support/nginx/gitlab origin/8-1-stable:lib/support/nginx/gitlab +``` + +If you are using Apache instead of NGINX please see the updated [Apache templates]. +Also note that because Apache does not support upstreams behind Unix sockets you +will need to let gitlab-git-http-server listen on a TCP port. You can do this +via [/etc/default/gitlab]. + +[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache +[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-1-stable/lib/support/init.d/gitlab.default.example#L34 + +### 8. Start application sudo service gitlab start sudo service nginx restart -### 8. Check application status +### 9. Check application status Check if GitLab and its environment are configured correctly: diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md index da719229ab6..593722eb01f 100644 --- a/doc/update/patch_versions.md +++ b/doc/update/patch_versions.md @@ -49,9 +49,7 @@ sudo -u git -H bundle install --without development test mysql --deployment sudo -u git -H bundle install --without development test postgres --deployment sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production -sudo -u git -H bundle exec rake assets:clean RAILS_ENV=production -sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production -sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production ``` ### 5. Start application diff --git a/features/steps/abuse_reports.rb b/features/steps/abuse_reports.rb index 56652ff6f05..499accb0b08 100644 --- a/features/steps/abuse_reports.rb +++ b/features/steps/abuse_reports.rb @@ -23,7 +23,7 @@ class Spinach::Features::AbuseReports < Spinach::FeatureSteps end step 'I should see a red "Report abuse" button' do - expect(find(:css, '.report_abuse')).to have_selector(:css, 'span.btn-close') + expect(page).to have_button("Already reported for abuse") end def user_mike diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/gitlab/markdown/reference_filter.rb index adaca78ba27..a4c560f578c 100644 --- a/lib/gitlab/markdown/reference_filter.rb +++ b/lib/gitlab/markdown/reference_filter.rb @@ -15,7 +15,7 @@ module Gitlab LazyReference = Struct.new(:klass, :ids) do def self.load(refs) lazy_references, values = refs.partition { |ref| ref.is_a?(self) } - + lazy_values = lazy_references.group_by(&:klass).flat_map do |klass, refs| ids = refs.flat_map(&:ids) klass.where(id: ids) @@ -107,10 +107,10 @@ module Gitlab return doc if project.nil? search_text_nodes(doc).each do |node| - content = node.to_html - - next unless content.match(pattern) next if ignored_ancestry?(node) + next unless node.text =~ pattern + + content = node.to_html html = yield content diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 606bf241db7..2e73f792a9d 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -335,7 +335,7 @@ namespace :gitlab do print "Redis version >= #{min_redis_version}? ... " redis_version = run(%W(redis-cli --version)) - redis_version = redis_version.try(:match, /redis-cli (.*)/) + redis_version = redis_version.try(:match, /redis-cli (\d+\.\d+\.\d+)/) if redis_version && (Gem::Version.new(redis_version[1]) > Gem::Version.new(min_redis_version)) puts "yes".green diff --git a/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb b/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb new file mode 100644 index 00000000000..34cd9f7e4eb --- /dev/null +++ b/spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe Gitlab::Markdown::ReferenceFilter, benchmark: true do + let(:input) do + html = <<-EOF +<p>Hello @alice and @bob, how are you doing today?</p> +<p>This is simple @dummy text to see how the @ReferenceFilter class performs +when @processing HTML.</p> + EOF + + Nokogiri::HTML.fragment(html) + end + + let(:project) { create(:empty_project) } + + let(:filter) { described_class.new(input, project: project) } + + describe '#replace_text_nodes_matching' do + let(:iterations) { 6000 } + + describe 'with identical input and output HTML' do + benchmark_subject do + filter.replace_text_nodes_matching(User.reference_pattern) do |content| + content + end + end + + it { is_expected.to iterate_per_second(iterations) } + end + + describe 'with different input and output HTML' do + benchmark_subject do + filter.replace_text_nodes_matching(User.reference_pattern) do |content| + '@eve' + end + end + + it { is_expected.to iterate_per_second(iterations) } + end + end +end diff --git a/spec/benchmarks/models/milestone_spec.rb b/spec/benchmarks/models/milestone_spec.rb new file mode 100644 index 00000000000..a94afc4c40d --- /dev/null +++ b/spec/benchmarks/models/milestone_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe Milestone, benchmark: true do + describe '#sort_issues' do + let(:milestone) { create(:milestone) } + + let(:issue1) { create(:issue, milestone: milestone) } + let(:issue2) { create(:issue, milestone: milestone) } + let(:issue3) { create(:issue, milestone: milestone) } + + let(:issue_ids) { [issue3.id, issue2.id, issue1.id] } + + benchmark_subject { milestone.sort_issues(issue_ids) } + + it { is_expected.to iterate_per_second(500) } + end +end diff --git a/spec/controllers/abuse_reports_controller_spec.rb b/spec/controllers/abuse_reports_controller_spec.rb new file mode 100644 index 00000000000..0faab8d7ff0 --- /dev/null +++ b/spec/controllers/abuse_reports_controller_spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' + +describe AbuseReportsController do + let(:reporter) { create(:user) } + let(:user) { create(:user) } + let(:message) { "This user is a spammer" } + + before do + sign_in(reporter) + end + + describe "POST create" do + context "with admin notification email set" do + let(:admin_email) { "admin@example.com"} + + before(:each) do + stub_application_setting(admin_notification_email: admin_email) + end + + it "sends a notification email" do + post :create, + abuse_report: { + user_id: user.id, + message: message + } + + email = ActionMailer::Base.deliveries.last + + expect(email.to).to eq([admin_email]) + expect(email.subject).to include(user.username) + expect(email.text_part.body).to include(message) + end + + it "saves the abuse report" do + expect do + post :create, + abuse_report: { + user_id: user.id, + message: message + } + end.to change { AbuseReport.count }.by(1) + end + end + + context "without admin notification email set" do + before(:each) do + stub_application_setting(admin_notification_email: nil) + end + + it "does not send a notification email" do + expect do + post :create, + abuse_report: { + user_id: user.id, + message: message + } + end.not_to change { ActionMailer::Base.deliveries.count } + end + + it "saves the abuse report" do + expect do + post :create, + abuse_report: { + user_id: user.id, + message: message + } + end.to change { AbuseReport.count }.by(1) + end + end + end + +end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 7168db117d6..fcbe62cace8 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -37,6 +37,32 @@ describe Admin::UsersController do end end + describe 'PUT block/:id' do + let(:user) { create(:user) } + + it 'blocks user' do + put :block, id: user.username + user.reload + expect(user.blocked?).to be_truthy + expect(flash[:notice]).to eq 'Successfully blocked' + end + end + + describe 'PUT unblock/:id' do + let(:user) { create(:user) } + + before do + user.block + end + + it 'unblocks user' do + put :unblock, id: user.username + user.reload + expect(user.blocked?).to be_falsey + expect(flash[:notice]).to eq 'Successfully unblocked' + end + end + describe 'PUT unlock/:id' do let(:user) { create(:user) } diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index 766be578f7f..bbf8adef534 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -41,7 +41,7 @@ describe Import::GithubController do it "assigns variables" do @project = create(:project, import_type: 'github', creator_id: user.id) - stub_client(repos: [@repo], orgs: [@org], org_repos: [@org_repo]) + stub_client(repos: [@repo, @org_repo], orgs: [@org], org_repos: [@org_repo]) get :status diff --git a/spec/controllers/invites_controller_spec.rb b/spec/controllers/invites_controller_spec.rb new file mode 100644 index 00000000000..3c6e54839b5 --- /dev/null +++ b/spec/controllers/invites_controller_spec.rb @@ -0,0 +1,33 @@ +require 'spec_helper' + +describe InvitesController do + let(:token) { '123456' } + let(:user) { create(:user) } + let(:member) { create(:project_member, invite_token: token, invite_email: 'test@abc.com', user: user) } + + before do + controller.instance_variable_set(:@member, member) + sign_in(user) + end + + describe 'GET #accept' do + it 'accepts user' do + get :accept, id: token + member.reload + + expect(response.status).to eq(302) + expect(member.user).to eq(user) + expect(flash[:notice]).to include 'You have been granted' + end + end + + describe 'GET #decline' do + it 'declines user' do + get :decline, id: token + expect{member.reload}.to raise_error ActiveRecord::RecordNotFound + + expect(response.status).to eq(302) + expect(flash[:notice]).to include 'You have declined the invitation to join' + end + end +end diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb index d4ecd98e12d..ccd8c741c83 100644 --- a/spec/controllers/projects/services_controller_spec.rb +++ b/spec/controllers/projects/services_controller_spec.rb @@ -10,26 +10,43 @@ describe Projects::ServicesController do project.team << [user, :master] controller.instance_variable_set(:@project, project) controller.instance_variable_set(:@service, service) - request.env["HTTP_REFERER"] = "/" end - describe "#test" do - context 'success' do - it "should redirect and show success message" do - expect(service).to receive(:test).and_return({ success: true, result: 'done' }) - get :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, format: :html - expect(response.status).to redirect_to('/') - expect(flash[:notice]).to eq('We sent a request to the provided URL') - end + shared_examples_for 'services controller' do |referrer| + before do + request.env["HTTP_REFERER"] = referrer end - context 'failure' do - it "should redirect and show failure message" do - expect(service).to receive(:test).and_return({ success: false, result: 'Bad test' }) - get :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, format: :html - expect(response.status).to redirect_to('/') - expect(flash[:alert]).to eq('We tried to send a request to the provided URL but an error occurred: Bad test') + describe "#test" do + context 'success' do + it "should redirect and show success message" do + expect(service).to receive(:test).and_return({ success: true, result: 'done' }) + get :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, format: :html + expect(response.status).to redirect_to('/') + expect(flash[:notice]).to eq('We sent a request to the provided URL') + end + end + + context 'failure' do + it "should redirect and show failure message" do + expect(service).to receive(:test).and_return({ success: false, result: 'Bad test' }) + get :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, format: :html + expect(response.status).to redirect_to('/') + expect(flash[:alert]).to eq('We tried to send a request to the provided URL but an error occurred: Bad test') + end end end end + + describe 'referrer defined' do + it_should_behave_like 'services controller' do + let!(:referrer) { "/" } + end + end + + describe 'referrer undefined' do + it_should_behave_like 'services controller' do + let!(:referrer) { nil } + end + end end diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index c88d5349663..77c58627322 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -140,4 +140,32 @@ describe Milestone do end end + describe '#sort_issues' do + let(:milestone) { create(:milestone) } + + let(:issue1) { create(:issue, milestone: milestone, position: 1) } + let(:issue2) { create(:issue, milestone: milestone, position: 2) } + let(:issue3) { create(:issue, milestone: milestone, position: 3) } + let(:issue4) { create(:issue, position: 42) } + + it 'sorts the given issues' do + milestone.sort_issues([issue3.id, issue2.id, issue1.id]) + + issue1.reload + issue2.reload + issue3.reload + + expect(issue1.position).to eq(3) + expect(issue2.position).to eq(2) + expect(issue3.position).to eq(1) + end + + it 'ignores issues not part of the milestone' do + milestone.sort_issues([issue3.id, issue2.id, issue1.id, issue4.id]) + + issue4.reload + + expect(issue4.position).to eq(42) + end + end end |