Merge branch 'master' into full-width-tables
This commit is contained in:
commit
d222ce8b34
68 changed files with 717 additions and 227 deletions
12
CHANGELOG
12
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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
font-family: $monospace_font;
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
padding: 1px 2px;
|
||||
}
|
||||
|
||||
kbd {
|
||||
|
|
|
@ -47,3 +47,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.calendar-hint {
|
||||
margin-top: -12px;
|
||||
float: right;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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: []
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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] })
|
||||
|
||||
items = items.where('id NOT IN (?)', item_ids)
|
||||
if projects
|
||||
items = items.where(milestones: { project_id: projects })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
items
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
12
app/mailers/abuse_report_mailer.rb
Normal file
12
app/mailers/abuse_report_mailer.rb
Normal file
|
@ -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
|
|
@ -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|
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
11
app/views/abuse_report_mailer/notify.html.haml
Normal file
11
app/views/abuse_report_mailer/notify.html.haml
Normal file
|
@ -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
|
5
app/views/abuse_report_mailer/notify.text.haml
Normal file
5
app/views/abuse_report_mailer/notify.text.haml
Normal file
|
@ -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}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
- if ci_build_details_path(@merge_request)
|
||||
= link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
|
||||
|
||||
- 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"
|
||||
.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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -6,47 +6,72 @@
|
|||
|
||||
= render 'shared/show_aside'
|
||||
|
||||
.row
|
||||
.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
|
||||
|
||||
|
||||
.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
|
||||
.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')
|
||||
|
||||
.username
|
||||
@#{@user.username}
|
||||
.description
|
||||
- if @user.bio.present?
|
||||
= @user.bio
|
||||
|
||||
.clearfix
|
||||
|
||||
- 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
|
||||
|
|
|
@ -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}",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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, [{
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddAdminNotificationEmailSetting < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :application_settings, :admin_notification_email, :string
|
||||
end
|
||||
end
|
|
@ -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|
|
||||
|
|
|
@ -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:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
41
spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb
Normal file
41
spec/benchmarks/lib/gitlab/markdown/reference_filter_spec.rb
Normal file
|
@ -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
|
17
spec/benchmarks/models/milestone_spec.rb
Normal file
17
spec/benchmarks/models/milestone_spec.rb
Normal file
|
@ -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
|
72
spec/controllers/abuse_reports_controller_spec.rb
Normal file
72
spec/controllers/abuse_reports_controller_spec.rb
Normal file
|
@ -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
|
|
@ -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) }
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
33
spec/controllers/invites_controller_spec.rb
Normal file
33
spec/controllers/invites_controller_spec.rb
Normal file
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue