summaryrefslogtreecommitdiff
path: root/app/controllers
diff options
context:
space:
mode:
authorSean McGivern <sean@gitlab.com>2016-08-18 15:49:32 +0100
committerSean McGivern <sean@gitlab.com>2016-08-18 15:54:07 +0100
commit8b1656282bcc39a0c1c7a3dccf74c98b1c3adae2 (patch)
treea5375c1ff8150d7777a120f29cfbd4d544ca4865 /app/controllers
parent21a73302e8a8b9f22e51f1707a306f04d3faad07 (diff)
parent2c1062f81e3c39cf8a45185c203995a43b91bf65 (diff)
downloadgitlab-ce-8b1656282bcc39a0c1c7a3dccf74c98b1c3adae2.tar.gz
Merge branch 'master' into expiration-date-on-memberships
Diffstat (limited to 'app/controllers')
-rw-r--r--app/controllers/admin/groups_controller.rb4
-rw-r--r--app/controllers/admin/spam_logs_controller.rb10
-rw-r--r--app/controllers/application_controller.rb56
-rw-r--r--app/controllers/autocomplete_controller.rb32
-rw-r--r--app/controllers/concerns/diff_for_path.rb6
-rw-r--r--app/controllers/concerns/issuable_collections.rb79
-rw-r--r--app/controllers/concerns/issues_action.rb10
-rw-r--r--app/controllers/concerns/merge_requests_action.rb10
-rw-r--r--app/controllers/concerns/service_params.rb19
-rw-r--r--app/controllers/concerns/spammable_actions.rb25
-rw-r--r--app/controllers/dashboard/todos_controller.rb4
-rw-r--r--app/controllers/groups_controller.rb4
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb12
-rw-r--r--app/controllers/profiles/passwords_controller.rb1
-rw-r--r--app/controllers/projects/badges_controller.rb18
-rw-r--r--app/controllers/projects/blob_controller.rb16
-rw-r--r--app/controllers/projects/board_lists_controller.rb65
-rw-r--r--app/controllers/projects/boards/application_controller.rb15
-rw-r--r--app/controllers/projects/boards/issues_controller.rb56
-rw-r--r--app/controllers/projects/boards/lists_controller.rb81
-rw-r--r--app/controllers/projects/boards_controller.rb15
-rw-r--r--app/controllers/projects/branches_controller.rb3
-rw-r--r--app/controllers/projects/builds_controller.rb2
-rw-r--r--app/controllers/projects/commit_controller.rb6
-rw-r--r--app/controllers/projects/compare_controller.rb16
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb20
-rw-r--r--app/controllers/projects/environments_controller.rb23
-rw-r--r--app/controllers/projects/git_http_client_controller.rb110
-rw-r--r--app/controllers/projects/git_http_controller.rb142
-rw-r--r--app/controllers/projects/hooks_controller.rb1
-rw-r--r--app/controllers/projects/issues_controller.rb18
-rw-r--r--app/controllers/projects/lfs_api_controller.rb94
-rw-r--r--app/controllers/projects/lfs_storage_controller.rb92
-rw-r--r--app/controllers/projects/merge_requests_controller.rb94
-rw-r--r--app/controllers/projects/pipelines_controller.rb2
-rw-r--r--app/controllers/projects/pipelines_settings_controller.rb8
-rw-r--r--app/controllers/projects/protected_branches_controller.rb26
-rw-r--r--app/controllers/projects/templates_controller.rb19
-rw-r--r--app/controllers/projects/wikis_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb4
-rw-r--r--app/controllers/registrations_controller.rb2
-rw-r--r--app/controllers/sessions_controller.rb2
42 files changed, 937 insertions, 287 deletions
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index f3a88a8e6c8..4ce18321649 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -48,9 +48,9 @@ class Admin::GroupsController < Admin::ApplicationController
end
def destroy
- DestroyGroupService.new(@group, current_user).execute
+ DestroyGroupService.new(@group, current_user).async_execute
- redirect_to admin_groups_path, notice: 'Group was successfully deleted.'
+ redirect_to admin_groups_path, alert: "Group '#{@group.name}' was scheduled for deletion."
end
private
diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb
index 3a2f0185315..2abfa22712d 100644
--- a/app/controllers/admin/spam_logs_controller.rb
+++ b/app/controllers/admin/spam_logs_controller.rb
@@ -14,4 +14,14 @@ class Admin::SpamLogsController < Admin::ApplicationController
head :ok
end
end
+
+ def mark_as_ham
+ spam_log = SpamLog.find(params[:id])
+
+ if HamService.new(spam_log).mark_as_ham!
+ redirect_to admin_spam_logs_path, notice: 'Spam log successfully submitted as ham.'
+ else
+ redirect_to admin_spam_logs_path, alert: 'Error with Akismet. Please check the logs for more info.'
+ end
+ end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index a1004d9bcea..634d36a4467 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -243,42 +243,6 @@ class ApplicationController < ActionController::Base
end
end
- def set_filters_params
- set_default_sort
-
- params[:scope] = 'all' if params[:scope].blank?
- params[:state] = 'opened' if params[:state].blank?
-
- @sort = params[:sort]
- @filter_params = params.dup
-
- if @project
- @filter_params[:project_id] = @project.id
- elsif @group
- @filter_params[:group_id] = @group.id
- else
- # TODO: this filter ignore issues/mr created in public or
- # internal repos where you are not a member. Enable this filter
- # or improve current implementation to filter only issues you
- # created or assigned or mentioned
- # @filter_params[:authorized_only] = true
- end
-
- @filter_params
- end
-
- def get_issues_collection
- set_filters_params
- @issuable_finder = IssuesFinder.new(current_user, @filter_params)
- @issuable_finder.execute
- end
-
- def get_merge_requests_collection
- set_filters_params
- @issuable_finder = MergeRequestsFinder.new(current_user, @filter_params)
- @issuable_finder.execute
- end
-
def import_sources_enabled?
!current_application_settings.import_sources.empty?
end
@@ -363,24 +327,4 @@ class ApplicationController < ActionController::Base
def u2f_app_id
request.base_url
end
-
- private
-
- def set_default_sort
- key = if is_a_listing_page_for?('issues') || is_a_listing_page_for?('merge_requests')
- 'issuable_sort'
- end
-
- cookies[key] = params[:sort] if key && params[:sort].present?
- params[:sort] = cookies[key] if key
- params[:sort] ||= 'id_desc'
- end
-
- def is_a_listing_page_for?(page_type)
- controller_name, action_name = params.values_at(:controller, :action)
-
- (controller_name == "projects/#{page_type}" && action_name == 'index') ||
- (controller_name == 'groups' && action_name == page_type) ||
- (controller_name == 'dashboard' && action_name == page_type)
- end
end
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index d828d163c28..b48668eea87 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -1,5 +1,6 @@
class AutocompleteController < ApplicationController
skip_before_action :authenticate_user!, only: [:users]
+ before_action :load_project, only: [:users]
before_action :find_users, only: [:users]
def users
@@ -34,19 +35,13 @@ class AutocompleteController < ApplicationController
def projects
project = Project.find_by_id(params[:project_id])
-
- projects = current_user.authorized_projects
- projects = projects.search(params[:search]) if params[:search].present?
- projects = projects.select do |project|
- current_user.can?(:admin_issue, project)
- end
+ projects = projects_finder.execute(project, search: params[:search], offset_id: params[:offset_id])
no_project = {
id: 0,
name_with_namespace: 'No project',
}
- projects.unshift(no_project)
- projects.delete(project)
+ projects.unshift(no_project) unless params[:offset_id].present?
render json: projects.to_json(only: [:id, :name_with_namespace], methods: :name_with_namespace)
end
@@ -55,11 +50,8 @@ class AutocompleteController < ApplicationController
def find_users
@users =
- if params[:project_id].present?
- project = Project.find(params[:project_id])
- return render_404 unless can?(current_user, :read_project, project)
-
- project.team.users
+ if @project
+ @project.team.users
elsif params[:group_id].present?
group = Group.find(params[:group_id])
return render_404 unless can?(current_user, :read_group, group)
@@ -71,4 +63,18 @@ class AutocompleteController < ApplicationController
User.none
end
end
+
+ def load_project
+ @project ||= begin
+ if params[:project_id].present?
+ project = Project.find(params[:project_id])
+ return render_404 unless can?(current_user, :read_project, project)
+ project
+ end
+ end
+ end
+
+ def projects_finder
+ MoveToProjectFinder.new(current_user)
+ end
end
diff --git a/app/controllers/concerns/diff_for_path.rb b/app/controllers/concerns/diff_for_path.rb
index 026d8b2e1e0..aeec3009f15 100644
--- a/app/controllers/concerns/diff_for_path.rb
+++ b/app/controllers/concerns/diff_for_path.rb
@@ -1,8 +1,8 @@
module DiffForPath
extend ActiveSupport::Concern
- def render_diff_for_path(diffs, diff_refs, project)
- diff_file = safe_diff_files(diffs, diff_refs: diff_refs, repository: project.repository).find do |diff|
+ def render_diff_for_path(diffs)
+ diff_file = diffs.diff_files.find do |diff|
diff.old_path == params[:old_path] && diff.new_path == params[:new_path]
end
@@ -14,7 +14,7 @@ module DiffForPath
locals = {
diff_file: diff_file,
diff_commit: diff_commit,
- diff_refs: diff_refs,
+ diff_refs: diffs.diff_refs,
blob: blob,
project: project
}
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
new file mode 100644
index 00000000000..c802922e0af
--- /dev/null
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -0,0 +1,79 @@
+module IssuableCollections
+ extend ActiveSupport::Concern
+ include SortingHelper
+
+ included do
+ helper_method :issues_finder
+ helper_method :merge_requests_finder
+ end
+
+ private
+
+ def issues_collection
+ issues_finder.execute
+ end
+
+ def merge_requests_collection
+ merge_requests_finder.execute
+ end
+
+ def issues_finder
+ @issues_finder ||= issuable_finder_for(IssuesFinder)
+ end
+
+ def merge_requests_finder
+ @merge_requests_finder ||= issuable_finder_for(MergeRequestsFinder)
+ end
+
+ def issuable_finder_for(finder_class)
+ finder_class.new(current_user, filter_params)
+ end
+
+ def filter_params
+ set_sort_order_from_cookie
+ set_default_scope
+ set_default_state
+
+ @filter_params = params.dup
+ @filter_params[:sort] ||= default_sort_order
+
+ @sort = @filter_params[:sort]
+
+ if @project
+ @filter_params[:project_id] = @project.id
+ elsif @group
+ @filter_params[:group_id] = @group.id
+ else
+ # TODO: this filter ignore issues/mr created in public or
+ # internal repos where you are not a member. Enable this filter
+ # or improve current implementation to filter only issues you
+ # created or assigned or mentioned
+ # @filter_params[:authorized_only] = true
+ end
+
+ @filter_params
+ end
+
+ def set_default_scope
+ params[:scope] = 'all' if params[:scope].blank?
+ end
+
+ def set_default_state
+ params[:state] = 'opened' if params[:state].blank?
+ end
+
+ def set_sort_order_from_cookie
+ key = 'issuable_sort'
+
+ cookies[key] = params[:sort] if params[:sort].present?
+ params[:sort] = cookies[key]
+ end
+
+ def default_sort_order
+ case params[:state]
+ when 'opened', 'all' then sort_value_recently_created
+ when 'merged', 'closed' then sort_value_recently_updated
+ else sort_value_recently_created
+ end
+ end
+end
diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb
index 4feabc32b1c..b89fb94be6e 100644
--- a/app/controllers/concerns/issues_action.rb
+++ b/app/controllers/concerns/issues_action.rb
@@ -1,12 +1,14 @@
module IssuesAction
extend ActiveSupport::Concern
+ include IssuableCollections
def issues
- @issues = get_issues_collection.non_archived
- @issues = @issues.page(params[:page])
- @issues = @issues.preload(:author, :project)
+ @label = issues_finder.labels.first
- @label = @issuable_finder.labels.first
+ @issues = issues_collection
+ .non_archived
+ .preload(:author, :project)
+ .page(params[:page])
respond_to do |format|
format.html
diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb
index 06a6b065e7e..a1b0eee37f9 100644
--- a/app/controllers/concerns/merge_requests_action.rb
+++ b/app/controllers/concerns/merge_requests_action.rb
@@ -1,11 +1,13 @@
module MergeRequestsAction
extend ActiveSupport::Concern
+ include IssuableCollections
def merge_requests
- @merge_requests = get_merge_requests_collection.non_archived
- @merge_requests = @merge_requests.page(params[:page])
- @merge_requests = @merge_requests.preload(:author, :target_project)
+ @label = merge_requests_finder.labels.first
- @label = @issuable_finder.labels.first
+ @merge_requests = merge_requests_collection
+ .non_archived
+ .preload(:author, :target_project)
+ .page(params[:page])
end
end
diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb
index 471d15af913..a69877edfd4 100644
--- a/app/controllers/concerns/service_params.rb
+++ b/app/controllers/concerns/service_params.rb
@@ -7,11 +7,16 @@ module ServiceParams
:build_key, :server, :teamcity_url, :drone_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
:colorize_messages, :channels,
- :push_events, :issues_events, :merge_requests_events, :tag_push_events,
- :note_events, :build_events, :wiki_page_events,
- :notify_only_broken_builds, :add_pusher,
- :send_from_committer_email, :disable_diffs, :external_wiki_url,
- :notify, :color,
+ # We're using `issues_events` and `merge_requests_events`
+ # in the view so we still need to explicitly state them
+ # here. `Service#event_names` would only give
+ # `issue_events` and `merge_request_events` (singular!)
+ # See app/helpers/services_helper.rb for how we
+ # make those event names plural as special case.
+ :issues_events, :merge_requests_events,
+ :notify_only_broken_builds, :notify_only_broken_pipelines,
+ :add_pusher, :send_from_committer_email, :disable_diffs,
+ :external_wiki_url, :notify, :color,
:server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
:jira_issue_transition_id]
@@ -19,9 +24,7 @@ module ServiceParams
FILTER_BLANK_PARAMS = [:password]
def service_params
- dynamic_params = []
- dynamic_params.concat(@service.event_channel_names)
-
+ dynamic_params = @service.event_channel_names + @service.event_names
service_params = params.permit(:id, service: ALLOWED_PARAMS + dynamic_params)
if service_params[:service].is_a?(Hash)
diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb
new file mode 100644
index 00000000000..29e243c66a3
--- /dev/null
+++ b/app/controllers/concerns/spammable_actions.rb
@@ -0,0 +1,25 @@
+module SpammableActions
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :authorize_submit_spammable!, only: :mark_as_spam
+ end
+
+ def mark_as_spam
+ if SpamService.new(spammable).mark_as_spam!
+ redirect_to spammable, notice: "#{spammable.class.to_s} was submitted to Akismet successfully."
+ else
+ redirect_to spammable, alert: 'Error with Akismet. Please check the logs for more info.'
+ end
+ end
+
+ private
+
+ def spammable
+ raise NotImplementedError, "#{self.class} does not implement #{__method__}"
+ end
+
+ def authorize_submit_spammable!
+ access_denied! unless current_user.admin?
+ end
+end
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 19a76a5b5d8..1243bb96d4d 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -37,8 +37,8 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def todos_counts
{
- count: TodosFinder.new(current_user, state: :pending).execute.count,
- done_count: TodosFinder.new(current_user, state: :done).execute.count
+ count: current_user.todos_pending_count,
+ done_count: current_user.todos_done_count
}
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 6780a6d4d87..cb82d62616c 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -87,9 +87,9 @@ class GroupsController < Groups::ApplicationController
end
def destroy
- DestroyGroupService.new(@group, current_user).execute
+ DestroyGroupService.new(@group, current_user).async_execute
- redirect_to root_path, alert: "Group '#{@group.name}' was successfully deleted."
+ redirect_to root_path, alert: "Group '#{@group.name}' was scheduled for deletion."
end
protected
diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb
index 30df1fb2fec..7d0eff37635 100644
--- a/app/controllers/import/gitlab_projects_controller.rb
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -1,5 +1,6 @@
class Import::GitlabProjectsController < Import::BaseController
before_action :verify_gitlab_project_import_enabled
+ before_action :authenticate_admin!
def new
@namespace_id = project_params[:namespace_id]
@@ -12,13 +13,14 @@ class Import::GitlabProjectsController < Import::BaseController
return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." })
end
- imported_file = project_params[:file].path + "-import"
+ import_upload_path = Gitlab::ImportExport.import_upload_path(filename: project_params[:file].original_filename)
- FileUtils.copy_entry(project_params[:file].path, imported_file)
+ FileUtils.mkdir_p(File.dirname(import_upload_path))
+ FileUtils.copy_entry(project_params[:file].path, import_upload_path)
@project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id],
current_user,
- File.expand_path(imported_file),
+ import_upload_path,
project_params[:path]).execute
if @project.saved?
@@ -46,4 +48,8 @@ class Import::GitlabProjectsController < Import::BaseController
:path, :namespace_id, :file
)
end
+
+ def authenticate_admin!
+ render_404 unless current_user.is_admin?
+ end
end
diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb
index c780e0983f9..6217ec5ecef 100644
--- a/app/controllers/profiles/passwords_controller.rb
+++ b/app/controllers/profiles/passwords_controller.rb
@@ -50,6 +50,7 @@ class Profiles::PasswordsController < Profiles::ApplicationController
flash[:notice] = "Password was successfully updated. Please login with it"
redirect_to new_user_session_path
else
+ @user.reload
render 'edit'
end
end
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index a9f482c8787..6c25cd83a24 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -4,12 +4,26 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :no_cache_headers, except: [:index]
def build
- badge = Gitlab::Badge::Build.new(project, params[:ref])
+ build_status = Gitlab::Badge::Build::Status
+ .new(project, params[:ref])
+ render_badge build_status
+ end
+
+ def coverage
+ coverage_report = Gitlab::Badge::Coverage::Report
+ .new(project, params[:ref], params[:job])
+
+ render_badge coverage_report
+ end
+
+ private
+
+ def render_badge(badge)
respond_to do |format|
format.html { render_404 }
format.svg do
- send_data(badge.data, type: badge.type, disposition: 'inline')
+ render 'badge', locals: { badge: badge.template }
end
end
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index eda3727a28d..cdf9a04bacf 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -17,6 +17,7 @@ class Projects::BlobController < Projects::ApplicationController
before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff]
before_action :validate_diff_params, only: :diff
+ before_action :set_last_commit_sha, only: [:edit, :update]
def new
commit unless @repository.empty?
@@ -33,7 +34,6 @@ class Projects::BlobController < Projects::ApplicationController
end
def edit
- @last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
blob.load_all_data!(@repository)
end
@@ -55,6 +55,10 @@ class Projects::BlobController < Projects::ApplicationController
create_commit(Files::UpdateService, success_path: after_edit_path,
failure_view: :edit,
failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
+
+ rescue Files::UpdateService::FileChangedError
+ @conflict = true
+ render :edit
end
def preview
@@ -76,6 +80,8 @@ class Projects::BlobController < Projects::ApplicationController
end
def diff
+ apply_diff_view_cookie!
+
@form = UnfoldForm.new(params)
@lines = Gitlab::Highlight.highlight_lines(repository, @ref, @path)
@lines = @lines[@form.since - 1..@form.to - 1]
@@ -150,7 +156,8 @@ class Projects::BlobController < Projects::ApplicationController
file_path: @file_path,
commit_message: params[:commit_message],
file_content: params[:content],
- file_content_encoding: params[:encoding]
+ file_content_encoding: params[:encoding],
+ last_commit_sha: params[:last_commit_sha]
}
end
@@ -159,4 +166,9 @@ class Projects::BlobController < Projects::ApplicationController
render nothing: true
end
end
+
+ def set_last_commit_sha
+ @last_commit_sha = Gitlab::Git::Commit.
+ last_for_path(@repository, @ref, @path).sha
+ end
end
diff --git a/app/controllers/projects/board_lists_controller.rb b/app/controllers/projects/board_lists_controller.rb
new file mode 100644
index 00000000000..3cfb08d5822
--- /dev/null
+++ b/app/controllers/projects/board_lists_controller.rb
@@ -0,0 +1,65 @@
+class Projects::BoardListsController < Projects::ApplicationController
+ respond_to :json
+
+ before_action :authorize_admin_list!
+
+ rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
+
+ def create
+ list = Boards::Lists::CreateService.new(project, current_user, list_params).execute
+
+ if list.valid?
+ render json: list.as_json(only: [:id, :list_type, :position], methods: [:title], include: { label: { only: [:id, :title, :description, :color, :priority] } })
+ else
+ render json: list.errors, status: :unprocessable_entity
+ end
+ end
+
+ def update
+ service = Boards::Lists::MoveService.new(project, current_user, move_params)
+
+ if service.execute
+ head :ok
+ else
+ head :unprocessable_entity
+ end
+ end
+
+ def destroy
+ service = Boards::Lists::DestroyService.new(project, current_user, params)
+
+ if service.execute
+ head :ok
+ else
+ head :unprocessable_entity
+ end
+ end
+
+ def generate
+ service = Boards::Lists::GenerateService.new(project, current_user)
+
+ if service.execute
+ render json: project.board.lists.label.as_json(only: [:id, :list_type, :position], methods: [:title], include: { label: { only: [:id, :title, :description, :color, :priority] } })
+ else
+ head :unprocessable_entity
+ end
+ end
+
+ private
+
+ def authorize_admin_list!
+ return render_403 unless can?(current_user, :admin_list, project)
+ end
+
+ def list_params
+ params.require(:list).permit(:label_id)
+ end
+
+ def move_params
+ params.require(:list).permit(:position).merge(id: params[:id])
+ end
+
+ def record_not_found(exception)
+ render json: { error: exception.message }, status: :not_found
+ end
+end
diff --git a/app/controllers/projects/boards/application_controller.rb b/app/controllers/projects/boards/application_controller.rb
new file mode 100644
index 00000000000..dad38fff6b9
--- /dev/null
+++ b/app/controllers/projects/boards/application_controller.rb
@@ -0,0 +1,15 @@
+module Projects
+ module Boards
+ class ApplicationController < Projects::ApplicationController
+ respond_to :json
+
+ rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
+
+ private
+
+ def record_not_found(exception)
+ render json: { error: exception.message }, status: :not_found
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/boards/issues_controller.rb b/app/controllers/projects/boards/issues_controller.rb
new file mode 100644
index 00000000000..2d894b3dd4a
--- /dev/null
+++ b/app/controllers/projects/boards/issues_controller.rb
@@ -0,0 +1,56 @@
+module Projects
+ module Boards
+ class IssuesController < Boards::ApplicationController
+ before_action :authorize_read_issue!, only: [:index]
+ before_action :authorize_update_issue!, only: [:update]
+
+ def index
+ issues = ::Boards::Issues::ListService.new(project, current_user, filter_params).execute
+ issues = issues.page(params[:page])
+
+ render json: issues.as_json(
+ only: [:iid, :title, :confidential],
+ include: {
+ assignee: { only: [:id, :name, :username], methods: [:avatar_url] },
+ labels: { only: [:id, :title, :description, :color, :priority] }
+ })
+ end
+
+ def update
+ service = ::Boards::Issues::MoveService.new(project, current_user, move_params)
+
+ if service.execute(issue)
+ head :ok
+ else
+ head :unprocessable_entity
+ end
+ end
+
+ private
+
+ def issue
+ @issue ||=
+ IssuesFinder.new(current_user, project_id: project.id, state: 'all')
+ .execute
+ .where(iid: params[:id])
+ .first!
+ end
+
+ def authorize_read_issue!
+ return render_403 unless can?(current_user, :read_issue, project)
+ end
+
+ def authorize_update_issue!
+ return render_403 unless can?(current_user, :update_issue, issue)
+ end
+
+ def filter_params
+ params.merge(id: params[:list_id])
+ end
+
+ def move_params
+ params.permit(:id, :from_list_id, :to_list_id)
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/boards/lists_controller.rb b/app/controllers/projects/boards/lists_controller.rb
new file mode 100644
index 00000000000..b995f586737
--- /dev/null
+++ b/app/controllers/projects/boards/lists_controller.rb
@@ -0,0 +1,81 @@
+module Projects
+ module Boards
+ class ListsController < Boards::ApplicationController
+ before_action :authorize_admin_list!, only: [:create, :update, :destroy, :generate]
+ before_action :authorize_read_list!, only: [:index]
+
+ def index
+ render json: serialize_as_json(project.board.lists)
+ end
+
+ def create
+ list = ::Boards::Lists::CreateService.new(project, current_user, list_params).execute
+
+ if list.valid?
+ render json: serialize_as_json(list)
+ else
+ render json: list.errors, status: :unprocessable_entity
+ end
+ end
+
+ def update
+ list = project.board.lists.movable.find(params[:id])
+ service = ::Boards::Lists::MoveService.new(project, current_user, move_params)
+
+ if service.execute(list)
+ head :ok
+ else
+ head :unprocessable_entity
+ end
+ end
+
+ def destroy
+ list = project.board.lists.destroyable.find(params[:id])
+ service = ::Boards::Lists::DestroyService.new(project, current_user, params)
+
+ if service.execute(list)
+ head :ok
+ else
+ head :unprocessable_entity
+ end
+ end
+
+ def generate
+ service = ::Boards::Lists::GenerateService.new(project, current_user)
+
+ if service.execute
+ render json: serialize_as_json(project.board.lists.movable)
+ else
+ head :unprocessable_entity
+ end
+ end
+
+ private
+
+ def authorize_admin_list!
+ return render_403 unless can?(current_user, :admin_list, project)
+ end
+
+ def authorize_read_list!
+ return render_403 unless can?(current_user, :read_list, project)
+ end
+
+ def list_params
+ params.require(:list).permit(:label_id)
+ end
+
+ def move_params
+ params.require(:list).permit(:position)
+ end
+
+ def serialize_as_json(resource)
+ resource.as_json(
+ only: [:id, :list_type, :position],
+ methods: [:title],
+ include: {
+ label: { only: [:id, :title, :description, :color, :priority] }
+ })
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
new file mode 100644
index 00000000000..33206717089
--- /dev/null
+++ b/app/controllers/projects/boards_controller.rb
@@ -0,0 +1,15 @@
+class Projects::BoardsController < Projects::ApplicationController
+ respond_to :html
+
+ before_action :authorize_read_board!, only: [:show]
+
+ def show
+ ::Boards::CreateService.new(project, current_user).execute
+ end
+
+ private
+
+ def authorize_read_board!
+ return access_denied! unless can?(current_user, :read_board, project)
+ end
+end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index e926043f3eb..48fe81b0d74 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -1,12 +1,13 @@
class Projects::BranchesController < Projects::ApplicationController
include ActionView::Helpers::SanitizeHelper
+ include SortingHelper
# Authorize
before_action :require_non_empty_project
before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:new, :create, :destroy]
def index
- @sort = params[:sort].presence || 'name'
+ @sort = params[:sort].presence || sort_value_name
@branches = BranchesFinder.new(@repository, params).execute
@branches = Kaminari.paginate_array(@branches).page(params[:page])
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 553b62741a5..12195c3cbb8 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -6,7 +6,7 @@ class Projects::BuildsController < Projects::ApplicationController
def index
@scope = params[:scope]
- @all_builds = project.builds
+ @all_builds = project.builds.relevant
@builds = @all_builds.order('created_at DESC')
@builds =
case @scope
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 7ae034f9398..f44e9bb3fd7 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -28,7 +28,7 @@ class Projects::CommitController < Projects::ApplicationController
end
def diff_for_path
- render_diff_for_path(@diffs, @commit.diff_refs, @project)
+ render_diff_for_path(@commit.diffs(diff_options))
end
def builds
@@ -134,8 +134,8 @@ class Projects::CommitController < Projects::ApplicationController
end
def define_status_vars
- @statuses = CommitStatus.where(pipeline: pipelines)
- @builds = Ci::Build.where(pipeline: pipelines)
+ @statuses = CommitStatus.where(pipeline: pipelines).relevant
+ @builds = Ci::Build.where(pipeline: pipelines).relevant
end
def assign_change_commit_vars(mr_source_branch)
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 8c004724f02..bee3d56076c 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -21,7 +21,7 @@ class Projects::CompareController < Projects::ApplicationController
def diff_for_path
return render_404 unless @compare
- render_diff_for_path(@diffs, @diff_refs, @project)
+ render_diff_for_path(@compare.diffs(diff_options))
end
def create
@@ -40,18 +40,12 @@ class Projects::CompareController < Projects::ApplicationController
@compare = CompareService.new.execute(@project, @head_ref, @project, @start_ref)
if @compare
- @commits = Commit.decorate(@compare.commits, @project)
-
- @start_commit = @project.commit(@start_ref)
- @commit = @project.commit(@head_ref)
- @base_commit = @project.merge_base_commit(@start_ref, @head_ref)
+ @commits = @compare.commits
+ @start_commit = @compare.start_commit
+ @commit = @compare.commit
+ @base_commit = @compare.base_commit
@diffs = @compare.diffs(diff_options)
- @diff_refs = Gitlab::Diff::DiffRefs.new(
- base_sha: @base_commit.try(:sha),
- start_sha: @start_commit.try(:sha),
- head_sha: @commit.try(:sha)
- )
@diff_notes_disabled = true
@grouped_diff_discussions = {}
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 83d5ced9be8..529e0aa2d33 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -12,8 +12,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def new
- redirect_to namespace_project_deploy_keys_path(@project.namespace,
- @project)
+ redirect_to namespace_project_deploy_keys_path(@project.namespace, @project)
end
def create
@@ -21,19 +20,16 @@ class Projects::DeployKeysController < Projects::ApplicationController
set_index_vars
if @key.valid? && @project.deploy_keys << @key
- redirect_to namespace_project_deploy_keys_path(@project.namespace,
- @project)
+ redirect_to namespace_project_deploy_keys_path(@project.namespace, @project)
else
render "index"
end
end
def enable
- @key = accessible_keys.find(params[:id])
- @project.deploy_keys << @key
+ Projects::EnableDeployKeyService.new(@project, current_user, params).execute
- redirect_to namespace_project_deploy_keys_path(@project.namespace,
- @project)
+ redirect_to namespace_project_deploy_keys_path(@project.namespace, @project)
end
def disable
@@ -45,9 +41,9 @@ class Projects::DeployKeysController < Projects::ApplicationController
protected
def set_index_vars
- @enabled_keys ||= @project.deploy_keys
+ @enabled_keys ||= @project.deploy_keys
- @available_keys ||= accessible_keys - @enabled_keys
+ @available_keys ||= current_user.accessible_deploy_keys - @enabled_keys
@available_project_keys ||= current_user.project_deploy_keys - @enabled_keys
@available_public_keys ||= DeployKey.are_public - @enabled_keys
@@ -56,10 +52,6 @@ class Projects::DeployKeysController < Projects::ApplicationController
@available_public_keys -= @available_project_keys
end
- def accessible_keys
- @accessible_keys ||= current_user.accessible_deploy_keys
- end
-
def deploy_key_params
params.require(:deploy_key).permit(:key, :title)
end
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 4b433796161..58678f96879 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -2,8 +2,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController
layout 'project'
before_action :authorize_read_environment!
before_action :authorize_create_environment!, only: [:new, :create]
- before_action :authorize_update_environment!, only: [:destroy]
- before_action :environment, only: [:show, :destroy]
+ before_action :authorize_update_environment!, only: [:edit, :update, :destroy]
+ before_action :environment, only: [:show, :edit, :update, :destroy]
def index
@environments = project.environments
@@ -17,13 +17,24 @@ class Projects::EnvironmentsController < Projects::ApplicationController
@environment = project.environments.new
end
+ def edit
+ end
+
def create
- @environment = project.environments.create(create_params)
+ @environment = project.environments.create(environment_params)
if @environment.persisted?
redirect_to namespace_project_environment_path(project.namespace, project, @environment)
else
- render 'new'
+ render :new
+ end
+ end
+
+ def update
+ if @environment.update(environment_params)
+ redirect_to namespace_project_environment_path(project.namespace, project, @environment)
+ else
+ render :edit
end
end
@@ -39,8 +50,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController
private
- def create_params
- params.require(:environment).permit(:name)
+ def environment_params
+ params.require(:environment).permit(:name, :external_url)
end
def environment
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
new file mode 100644
index 00000000000..7c21bd181dc
--- /dev/null
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -0,0 +1,110 @@
+# This file should be identical in GitLab Community Edition and Enterprise Edition
+
+class Projects::GitHttpClientController < Projects::ApplicationController
+ include ActionController::HttpAuthentication::Basic
+ include KerberosSpnegoHelper
+
+ attr_reader :user
+
+ # Git clients will not know what authenticity token to send along
+ skip_before_action :verify_authenticity_token
+ skip_before_action :repository
+ before_action :authenticate_user
+ before_action :ensure_project_found!
+
+ private
+
+ def authenticate_user
+ if project && project.public? && download_request?
+ return # Allow access
+ end
+
+ if allow_basic_auth? && basic_auth_provided?
+ login, password = user_name_and_password(request)
+ auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
+
+ if auth_result.type == :ci && download_request?
+ @ci = true
+ elsif auth_result.type == :oauth && !download_request?
+ # Not allowed
+ else
+ @user = auth_result.user
+ end
+
+ if ci? || user
+ return # Allow access
+ end
+ elsif allow_kerberos_spnego_auth? && spnego_provided?
+ @user = find_kerberos_user
+
+ if user
+ send_final_spnego_response
+ return # Allow access
+ end
+ end
+
+ send_challenges
+ render plain: "HTTP Basic: Access denied\n", status: 401
+ end
+
+ def basic_auth_provided?
+ has_basic_credentials?(request)
+ end
+
+ def send_challenges
+ challenges = []
+ challenges << 'Basic realm="GitLab"' if allow_basic_auth?
+ challenges << spnego_challenge if allow_kerberos_spnego_auth?
+ headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
+ end
+
+ def ensure_project_found!
+ render_not_found if project.blank?
+ end
+
+ def project
+ return @project if defined?(@project)
+
+ project_id, _ = project_id_with_suffix
+ if project_id.blank?
+ @project = nil
+ else
+ @project = Project.find_with_namespace("#{params[:namespace_id]}/#{project_id}")
+ end
+ end
+
+ # This method returns two values so that we can parse
+ # params[:project_id] (untrusted input!) in exactly one place.
+ def project_id_with_suffix
+ id = params[:project_id] || ''
+
+ %w[.wiki.git .git].each do |suffix|
+ if id.end_with?(suffix)
+ # Be careful to only remove the suffix from the end of 'id'.
+ # Accidentally removing it from the middle is how security
+ # vulnerabilities happen!
+ return [id.slice(0, id.length - suffix.length), suffix]
+ end
+ end
+
+ # Something is wrong with params[:project_id]; do not pass it on.
+ [nil, nil]
+ end
+
+ def repository
+ _, suffix = project_id_with_suffix
+ if suffix == '.wiki.git'
+ project.wiki.repository
+ else
+ project.repository
+ end
+ end
+
+ def render_not_found
+ render plain: 'Not Found', status: :not_found
+ end
+
+ def ci?
+ @ci.present?
+ end
+end
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index 40a8b7940d9..b4373ef89ef 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -1,17 +1,6 @@
# This file should be identical in GitLab Community Edition and Enterprise Edition
-class Projects::GitHttpController < Projects::ApplicationController
- include ActionController::HttpAuthentication::Basic
- include KerberosSpnegoHelper
-
- attr_reader :user
-
- # Git clients will not know what authenticity token to send along
- skip_before_action :verify_authenticity_token
- skip_before_action :repository
- before_action :authenticate_user
- before_action :ensure_project_found!
-
+class Projects::GitHttpController < Projects::GitHttpClientController
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
# GET /foo/bar.git/info/refs?service=git-receive-pack (git push)
def info_refs
@@ -20,9 +9,9 @@ class Projects::GitHttpController < Projects::ApplicationController
elsif receive_pack? && receive_pack_allowed?
render_ok
elsif http_blocked?
- render_not_allowed
+ render_http_not_allowed
else
- render_not_found
+ render_denied
end
end
@@ -31,7 +20,7 @@ class Projects::GitHttpController < Projects::ApplicationController
if upload_pack? && upload_pack_allowed?
render_ok
else
- render_not_found
+ render_denied
end
end
@@ -40,87 +29,14 @@ class Projects::GitHttpController < Projects::ApplicationController
if receive_pack? && receive_pack_allowed?
render_ok
else
- render_not_found
+ render_denied
end
end
private
- def authenticate_user
- if project && project.public? && upload_pack?
- return # Allow access
- end
-
- if allow_basic_auth? && basic_auth_provided?
- login, password = user_name_and_password(request)
- auth_result = Gitlab::Auth.find_for_git_client(login, password, project: project, ip: request.ip)
-
- if auth_result.type == :ci && upload_pack?
- @ci = true
- elsif auth_result.type == :oauth && !upload_pack?
- # Not allowed
- else
- @user = auth_result.user
- end
-
- if ci? || user
- return # Allow access
- end
- elsif allow_kerberos_spnego_auth? && spnego_provided?
- @user = find_kerberos_user
-
- if user
- send_final_spnego_response
- return # Allow access
- end
- end
-
- send_challenges
- render plain: "HTTP Basic: Access denied\n", status: 401
- end
-
- def basic_auth_provided?
- has_basic_credentials?(request)
- end
-
- def send_challenges
- challenges = []
- challenges << 'Basic realm="GitLab"' if allow_basic_auth?
- challenges << spnego_challenge if allow_kerberos_spnego_auth?
- headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
- end
-
- def ensure_project_found!
- render_not_found if project.blank?
- end
-
- def project
- return @project if defined?(@project)
-
- project_id, _ = project_id_with_suffix
- if project_id.blank?
- @project = nil
- else
- @project = Project.find_with_namespace("#{params[:namespace_id]}/#{project_id}")
- end
- end
-
- # This method returns two values so that we can parse
- # params[:project_id] (untrusted input!) in exactly one place.
- def project_id_with_suffix
- id = params[:project_id] || ''
-
- %w[.wiki.git .git].each do |suffix|
- if id.end_with?(suffix)
- # Be careful to only remove the suffix from the end of 'id'.
- # Accidentally removing it from the middle is how security
- # vulnerabilities happen!
- return [id.slice(0, id.length - suffix.length), suffix]
- end
- end
-
- # Something is wrong with params[:project_id]; do not pass it on.
- [nil, nil]
+ def download_request?
+ upload_pack?
end
def upload_pack?
@@ -143,47 +59,37 @@ class Projects::GitHttpController < Projects::ApplicationController
render json: Gitlab::Workhorse.git_http_ok(repository, user)
end
- def repository
- _, suffix = project_id_with_suffix
- if suffix == '.wiki.git'
- project.wiki.repository
- else
- project.repository
- end
- end
-
- def render_not_found
- render plain: 'Not Found', status: :not_found
+ def render_http_not_allowed
+ render plain: access_check.message, status: :forbidden
end
- def render_not_allowed
- render plain: download_access.message, status: :forbidden
- end
-
- def ci?
- @ci.present?
+ def render_denied
+ if user && user.can?(:read_project, project)
+ render plain: 'Access denied', status: :forbidden
+ else
+ # Do not leak information about project existence
+ render_not_found
+ end
end
def upload_pack_allowed?
return false unless Gitlab.config.gitlab_shell.upload_pack
if user
- download_access.allowed?
+ access_check.allowed?
else
ci? || project.public?
end
end
def access
- return @access if defined?(@access)
-
- @access = Gitlab::GitAccess.new(user, project, 'http')
+ @access ||= Gitlab::GitAccess.new(user, project, 'http')
end
- def download_access
- return @download_access if defined?(@download_access)
-
- @download_access = access.check('git-upload-pack')
+ def access_check
+ # Use the magic string '_any' to indicate we do not know what the
+ # changes are. This is also what gitlab-shell does.
+ @access_check ||= access.check(git_command, '_any')
end
def http_blocked?
@@ -193,8 +99,6 @@ class Projects::GitHttpController < Projects::ApplicationController
def receive_pack_allowed?
return false unless Gitlab.config.gitlab_shell.receive_pack
- # Skip user authorization on upload request.
- # It will be done by the pre-receive hook in the repository.
- user.present?
+ access_check.allowed?
end
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index a60027ff477..b5624046387 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -56,6 +56,7 @@ class Projects::HooksController < Projects::ApplicationController
def hook_params
params.require(:hook).permit(
:build_events,
+ :pipeline_events,
:enable_ssl_verification,
:issues_events,
:merge_requests_events,
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 3c6f29ac0ba..e9fb11e8f94 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -3,7 +3,10 @@ class Projects::IssuesController < Projects::ApplicationController
include ToggleSubscriptionAction
include IssuableActions
include ToggleAwardEmoji
+ include IssuableCollections
+ include SpammableActions
+ before_action :redirect_to_external_issue_tracker, only: [:index, :new]
before_action :module_enabled
before_action :issue, only: [:edit, :update, :show, :referenced_merge_requests,
:related_branches, :can_create_branch]
@@ -24,7 +27,7 @@ class Projects::IssuesController < Projects::ApplicationController
def index
terms = params['issue_search']
- @issues = get_issues_collection
+ @issues = issues_collection
if terms.present?
if terms =~ /\A#(\d+)\z/
@@ -183,6 +186,7 @@ class Projects::IssuesController < Projects::ApplicationController
alias_method :subscribable_resource, :issue
alias_method :issuable, :issue
alias_method :awardable, :issue
+ alias_method :spammable, :issue
def authorize_read_issue!
return render_404 unless can?(current_user, :read_issue, @issue)
@@ -200,6 +204,18 @@ class Projects::IssuesController < Projects::ApplicationController
return render_404 unless @project.issues_enabled && @project.default_issues_tracker?
end
+ def redirect_to_external_issue_tracker
+ external = @project.external_issue_tracker
+
+ return unless external
+
+ if action_name == 'new'
+ redirect_to external.new_issue_path
+ else
+ redirect_to external.issues_url
+ end
+ end
+
# Since iids are implemented only in 6.1
# user may navigate to issue page using old global ids.
#
diff --git a/app/controllers/projects/lfs_api_controller.rb b/app/controllers/projects/lfs_api_controller.rb
new file mode 100644
index 00000000000..ece49dcd922
--- /dev/null
+++ b/app/controllers/projects/lfs_api_controller.rb
@@ -0,0 +1,94 @@
+class Projects::LfsApiController < Projects::GitHttpClientController
+ include LfsHelper
+
+ before_action :require_lfs_enabled!
+ before_action :lfs_check_access!, except: [:deprecated]
+
+ def batch
+ unless objects.present?
+ render_lfs_not_found
+ return
+ end
+
+ if download_request?
+ render json: { objects: download_objects! }
+ elsif upload_request?
+ render json: { objects: upload_objects! }
+ else
+ raise "Never reached"
+ end
+ end
+
+ def deprecated
+ render(
+ json: {
+ message: 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
+ documentation_url: "#{Gitlab.config.gitlab.url}/help",
+ },
+ status: 501
+ )
+ end
+
+ private
+
+ def objects
+ @objects ||= (params[:objects] || []).to_a
+ end
+
+ def existing_oids
+ @existing_oids ||= begin
+ storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
+ end
+ end
+
+ def download_objects!
+ objects.each do |object|
+ if existing_oids.include?(object[:oid])
+ object[:actions] = download_actions(object)
+ else
+ object[:error] = {
+ code: 404,
+ message: "Object does not exist on the server or you don't have permissions to access it",
+ }
+ end
+ end
+ objects
+ end
+
+ def upload_objects!
+ objects.each do |object|
+ object[:actions] = upload_actions(object) unless existing_oids.include?(object[:oid])
+ end
+ objects
+ end
+
+ def download_actions(object)
+ {
+ download: {
+ href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}",
+ header: {
+ Authorization: request.headers['Authorization']
+ }.compact
+ }
+ }
+ end
+
+ def upload_actions(object)
+ {
+ upload: {
+ href: "#{project.http_url_to_repo}/gitlab-lfs/objects/#{object[:oid]}/#{object[:size]}",
+ header: {
+ Authorization: request.headers['Authorization']
+ }.compact
+ }
+ }
+ end
+
+ def download_request?
+ params[:operation] == 'download'
+ end
+
+ def upload_request?
+ params[:operation] == 'upload'
+ end
+end
diff --git a/app/controllers/projects/lfs_storage_controller.rb b/app/controllers/projects/lfs_storage_controller.rb
new file mode 100644
index 00000000000..69066cb40e6
--- /dev/null
+++ b/app/controllers/projects/lfs_storage_controller.rb
@@ -0,0 +1,92 @@
+class Projects::LfsStorageController < Projects::GitHttpClientController
+ include LfsHelper
+
+ before_action :require_lfs_enabled!
+ before_action :lfs_check_access!
+
+ def download
+ lfs_object = LfsObject.find_by_oid(oid)
+ unless lfs_object && lfs_object.file.exists?
+ render_lfs_not_found
+ return
+ end
+
+ send_file lfs_object.file.path, content_type: "application/octet-stream"
+ end
+
+ def upload_authorize
+ render(
+ json: {
+ StoreLFSPath: "#{Gitlab.config.lfs.storage_path}/tmp/upload",
+ LfsOid: oid,
+ LfsSize: size,
+ },
+ content_type: 'application/json; charset=utf-8'
+ )
+ end
+
+ def upload_finalize
+ unless tmp_filename
+ render_lfs_forbidden
+ return
+ end
+
+ if store_file(oid, size, tmp_filename)
+ head 200
+ else
+ render plain: 'Unprocessable entity', status: 422
+ end
+ end
+
+ private
+
+ def download_request?
+ action_name == 'download'
+ end
+
+ def upload_request?
+ %w[upload_authorize upload_finalize].include? action_name
+ end
+
+ def oid
+ params[:oid].to_s
+ end
+
+ def size
+ params[:size].to_i
+ end
+
+ def tmp_filename
+ name = request.headers['X-Gitlab-Lfs-Tmp']
+ return if name.include?('/')
+ return unless oid.present? && name.start_with?(oid)
+ name
+ end
+
+ def store_file(oid, size, tmp_file)
+ # Define tmp_file_path early because we use it in "ensure"
+ tmp_file_path = File.join("#{Gitlab.config.lfs.storage_path}/tmp/upload", tmp_file)
+
+ object = LfsObject.find_or_create_by(oid: oid, size: size)
+ file_exists = object.file.exists? || move_tmp_file_to_storage(object, tmp_file_path)
+ file_exists && link_to_project(object)
+ ensure
+ FileUtils.rm_f(tmp_file_path)
+ end
+
+ def move_tmp_file_to_storage(object, path)
+ File.open(path) do |f|
+ object.file = f
+ end
+
+ object.file.store!
+ object.save
+ end
+
+ def link_to_project(object)
+ if object && !object.projects.exists?(storage_project.id)
+ object.projects << storage_project
+ object.save
+ end
+ end
+end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 47c21a18b33..00a3022cbf7 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -5,18 +5,19 @@ class Projects::MergeRequestsController < Projects::ApplicationController
include IssuableActions
include NotesHelper
include ToggleAwardEmoji
+ include IssuableCollections
before_action :module_enabled
before_action :merge_request, only: [
- :edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check,
- :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip
+ :edit, :update, :show, :diffs, :commits, :conflicts, :builds, :pipelines, :merge, :merge_check,
+ :ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip, :resolve_conflicts
]
- before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
- before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
+ before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds, :pipelines]
+ before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :builds, :pipelines]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs]
before_action :define_diff_comment_vars, only: [:diffs]
- before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds]
+ before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds, :conflicts, :pipelines]
# Allow read any merge_request
before_action :authorize_read_merge_request!
@@ -27,9 +28,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
# Allow modify merge_request
before_action :authorize_update_merge_request!, only: [:close, :edit, :update, :remove_wip, :sort]
+ before_action :authorize_can_resolve_conflicts!, only: [:conflicts, :resolve_conflicts]
+
def index
terms = params['issue_search']
- @merge_requests = get_merge_requests_collection
+ @merge_requests = merge_requests_collection
if terms.present?
if terms =~ /\A[#!](\d+)\z/
@@ -84,7 +87,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format|
format.html { define_discussion_vars }
- format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
+ format.json do
+ @diffs = @merge_request.diffs(diff_options)
+
+ render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") }
+ end
end
end
@@ -102,9 +109,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
define_commit_vars
- diffs = @merge_request.diffs(diff_options)
- render_diff_for_path(diffs, @merge_request.diff_refs, @merge_request.project)
+ render_diff_for_path(@merge_request.diffs(diff_options))
end
def commits
@@ -126,6 +132,47 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
+ def conflicts
+ respond_to do |format|
+ format.html { define_discussion_vars }
+
+ format.json do
+ if @merge_request.conflicts_can_be_resolved_in_ui?
+ render json: @merge_request.conflicts
+ elsif @merge_request.can_be_merged?
+ render json: {
+ message: 'The merge conflicts for this merge request have already been resolved. Please return to the merge request.',
+ type: 'error'
+ }
+ else
+ render json: {
+ message: 'The merge conflicts for this merge request cannot be resolved through GitLab. Please try to resolve them locally.',
+ type: 'error'
+ }
+ end
+ end
+ end
+ end
+
+ def resolve_conflicts
+ return render_404 unless @merge_request.conflicts_can_be_resolved_in_ui?
+
+ if @merge_request.can_be_merged?
+ render status: :bad_request, json: { message: 'The merge conflicts for this merge request have already been resolved.' }
+ return
+ end
+
+ begin
+ MergeRequests::ResolveService.new(@merge_request.source_project, current_user, params).execute(@merge_request)
+
+ flash[:notice] = 'All merge conflicts were resolved. The merge request can now be merged.'
+
+ render json: { redirect_to: namespace_project_merge_request_url(@project.namespace, @project, @merge_request, resolved_conflicts: true) }
+ rescue Gitlab::Conflict::File::MissingResolution => e
+ render status: :bad_request, json: { message: e.message }
+ end
+ end
+
def builds
respond_to do |format|
format.html do
@@ -137,7 +184,22 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
end
+ def pipelines
+ @pipelines = @merge_request.all_pipelines
+
+ respond_to do |format|
+ format.html do
+ define_discussion_vars
+
+ render 'show'
+ end
+ format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_pipelines') } }
+ end
+ end
+
def new
+ apply_diff_view_cookie!
+
build_merge_request
@noteable = @merge_request
@@ -152,11 +214,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commits = @merge_request.compare_commits.reverse
@commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit
- @diffs = @merge_request.compare.diffs(diff_options) if @merge_request.compare
+ @diffs = @merge_request.diffs(diff_options) if @merge_request.compare
@diff_notes_disabled = true
@pipeline = @merge_request.pipeline
- @statuses = @pipeline.statuses if @pipeline
+ @statuses = @pipeline.statuses.relevant if @pipeline
@note_counts = Note.where(commit_id: @commits.map(&:id)).
group(:commit_id).count
@@ -334,6 +396,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
return render_404 unless can?(current_user, :admin_merge_request, @merge_request)
end
+ def authorize_can_resolve_conflicts!
+ return render_404 unless @merge_request.conflicts_can_be_resolved_by?(current_user)
+ end
+
def module_enabled
return render_404 unless @project.merge_requests_enabled
end
@@ -358,7 +424,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@commits_count = @merge_request.commits.count
@pipeline = @merge_request.pipeline
- @statuses = @pipeline.statuses if @pipeline
+ @statuses = @pipeline.statuses.relevant if @pipeline
if @merge_request.locked_long_ago?
@merge_request.unlock_mr
@@ -377,6 +443,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
fresh.
discussions
+ preload_noteable_for_regular_notes(@discussions.flat_map(&:notes))
+
# This is not executed lazily
@notes = Banzai::NoteRenderer.render(
@discussions.flat_map(&:notes),
@@ -406,7 +474,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
noteable_id: @merge_request.id
}
- @use_legacy_diff_notes = !@merge_request.support_new_diff_notes?
+ @use_legacy_diff_notes = !@merge_request.has_complete_diff_refs?
@grouped_diff_discussions = @merge_request.notes.inc_author_project_award_emoji.grouped_diff_discussions
Banzai::NoteRenderer.render(
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 487963fdcd7..b0c72cfe4b4 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -19,7 +19,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def create
- @pipeline = Ci::CreatePipelineService.new(project, current_user, create_params).execute
+ @pipeline = Ci::CreatePipelineService.new(project, current_user, create_params).execute(ignore_skip_ci: true, save_on_errors: false)
unless @pipeline.persisted?
render 'new'
return
diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb
index 85ba706e5cd..9136633b87a 100644
--- a/app/controllers/projects/pipelines_settings_controller.rb
+++ b/app/controllers/projects/pipelines_settings_controller.rb
@@ -3,7 +3,13 @@ class Projects::PipelinesSettingsController < Projects::ApplicationController
def show
@ref = params[:ref] || @project.default_branch || 'master'
- @build_badge = Gitlab::Badge::Build.new(@project, @ref)
+
+ @badges = [Gitlab::Badge::Build::Status,
+ Gitlab::Badge::Coverage::Report]
+
+ @badges.map! do |badge|
+ badge.new(@project, @ref).metadata
+ end
end
def update
diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb
index d28ec6e2eac..9a438d5512c 100644
--- a/app/controllers/projects/protected_branches_controller.rb
+++ b/app/controllers/projects/protected_branches_controller.rb
@@ -9,16 +9,16 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
def index
@protected_branch = @project.protected_branches.new
- load_protected_branches_gon_variables
+ load_gon_index
end
def create
- @protected_branch = ProtectedBranches::CreateService.new(@project, current_user, protected_branch_params).execute
+ @protected_branch = ::ProtectedBranches::CreateService.new(@project, current_user, protected_branch_params).execute
if @protected_branch.persisted?
redirect_to namespace_project_protected_branches_path(@project.namespace, @project)
else
load_protected_branches
- load_protected_branches_gon_variables
+ load_gon_index
render :index
end
end
@@ -28,7 +28,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
end
def update
- @protected_branch = ProtectedBranches::UpdateService.new(@project, current_user, protected_branch_params).execute(@protected_branch)
+ @protected_branch = ::ProtectedBranches::UpdateService.new(@project, current_user, protected_branch_params).execute(@protected_branch)
if @protected_branch.valid?
respond_to do |format|
@@ -58,17 +58,23 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
def protected_branch_params
params.require(:protected_branch).permit(:name,
- merge_access_level_attributes: [:access_level],
- push_access_level_attributes: [:access_level])
+ merge_access_levels_attributes: [:access_level, :id],
+ push_access_levels_attributes: [:access_level, :id])
end
def load_protected_branches
@protected_branches = @project.protected_branches.order(:name).page(params[:page])
end
- def load_protected_branches_gon_variables
- gon.push({ open_branches: @project.open_branches.map { |br| { text: br.name, id: br.name, title: br.name } },
- push_access_levels: ProtectedBranch::PushAccessLevel.human_access_levels.map { |id, text| { id: id, text: text } },
- merge_access_levels: ProtectedBranch::MergeAccessLevel.human_access_levels.map { |id, text| { id: id, text: text } } })
+ def access_levels_options
+ {
+ push_access_levels: ProtectedBranch::PushAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } },
+ merge_access_levels: ProtectedBranch::MergeAccessLevel.human_access_levels.map { |id, text| { id: id, text: text, before_divider: true } }
+ }
+ end
+
+ def load_gon_index
+ params = { open_branches: @project.open_branches.map { |br| { text: br.name, id: br.name, title: br.name } } }
+ gon.push(params.merge(access_levels_options))
end
end
diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb
new file mode 100644
index 00000000000..694b468c8d3
--- /dev/null
+++ b/app/controllers/projects/templates_controller.rb
@@ -0,0 +1,19 @@
+class Projects::TemplatesController < Projects::ApplicationController
+ before_action :authenticate_user!, :get_template_class
+
+ def show
+ template = @template_type.find(params[:key], project)
+
+ respond_to do |format|
+ format.json { render json: template.to_json }
+ end
+ end
+
+ private
+
+ def get_template_class
+ template_types = { issue: Gitlab::Template::IssueTemplate, merge_request: Gitlab::Template::MergeRequestTemplate }.with_indifferent_access
+ @template_type = template_types[params[:template_type]]
+ render json: [], status: 404 unless @template_type
+ end
+end
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 607fe9c7fed..177ccf5eec9 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -91,7 +91,7 @@ class Projects::WikisController < Projects::ApplicationController
)
end
- def markdown_preview
+ def preview_markdown
text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user)
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index a6e1aa5ccc1..47efbd4a939 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -125,7 +125,7 @@ class ProjectsController < Projects::ApplicationController
def destroy
return access_denied! unless can?(current_user, :remove_project, @project)
- ::Projects::DestroyService.new(@project, current_user, {}).pending_delete!
+ ::Projects::DestroyService.new(@project, current_user, {}).async_execute
flash[:alert] = "Project '#{@project.name}' will be deleted."
redirect_to dashboard_projects_path
@@ -238,7 +238,7 @@ class ProjectsController < Projects::ApplicationController
}
end
- def markdown_preview
+ def preview_markdown
text = params[:text]
ext = Gitlab::ReferenceExtractor.new(@project, current_user)
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 75b78a49eab..3327f4f2b87 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -33,7 +33,7 @@ class RegistrationsController < Devise::RegistrationsController
protected
- def build_resource(hash=nil)
+ def build_resource(hash = nil)
super
end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 17aed816cbd..5d7ecfeacf4 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -101,7 +101,7 @@ class SessionsController < Devise::SessionsController
# Prevent alert from popping up on the first page shown after authentication.
flash[:alert] = nil
- redirect_to user_omniauth_authorize_path(provider.to_sym)
+ redirect_to omniauth_authorize_path(:user, provider)
end
def valid_otp_attempt?(user)