Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-06-18 14:18:50 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-06-18 14:18:50 +0300
commit8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch)
treea77e7fe7a93de11213032ed4ab1f33a3db51b738 /app/controllers
parent00b35af3db1abfe813a778f643dad221aad51fca (diff)
Add latest changes from gitlab-org/gitlab@13-1-stable-ee
Diffstat (limited to 'app/controllers')
-rw-r--r--app/controllers/admin/application_settings_controller.rb5
-rw-r--r--app/controllers/admin/runners_controller.rb2
-rw-r--r--app/controllers/admin/users_controller.rb3
-rw-r--r--app/controllers/application_controller.rb1
-rw-r--r--app/controllers/clusters/base_controller.rb2
-rw-r--r--app/controllers/clusters/clusters_controller.rb1
-rw-r--r--app/controllers/concerns/enforces_two_factor_authentication.rb3
-rw-r--r--app/controllers/concerns/find_snippet.rb24
-rw-r--r--app/controllers/concerns/integrations_actions.rb6
-rw-r--r--app/controllers/concerns/issuable_actions.rb29
-rw-r--r--app/controllers/concerns/issuable_collections.rb3
-rw-r--r--app/controllers/concerns/issuable_collections_action.rb4
-rw-r--r--app/controllers/concerns/known_sign_in.rb2
-rw-r--r--app/controllers/concerns/milestone_actions.rb8
-rw-r--r--app/controllers/concerns/notes_actions.rb6
-rw-r--r--app/controllers/concerns/preview_markdown.rb2
-rw-r--r--app/controllers/concerns/service_params.rb3
-rw-r--r--app/controllers/concerns/snippet_authorizations.rb23
-rw-r--r--app/controllers/concerns/snippets_actions.rb61
-rw-r--r--app/controllers/concerns/wiki_actions.rb232
-rw-r--r--app/controllers/concerns/workhorse_import_export_upload.rb33
-rw-r--r--app/controllers/dashboard/milestones_controller.rb32
-rw-r--r--app/controllers/dashboard/todos_controller.rb10
-rw-r--r--app/controllers/groups/boards_controller.rb1
-rw-r--r--app/controllers/groups/group_links_controller.rb2
-rw-r--r--app/controllers/groups/imports_controller.rb19
-rw-r--r--app/controllers/groups/milestones_controller.rb71
-rw-r--r--app/controllers/groups_controller.rb15
-rw-r--r--app/controllers/ide_controller.rb6
-rw-r--r--app/controllers/import/base_controller.rb76
-rw-r--r--app/controllers/import/bitbucket_controller.rb53
-rw-r--r--app/controllers/import/bitbucket_server_controller.rb77
-rw-r--r--app/controllers/import/fogbugz_controller.rb33
-rw-r--r--app/controllers/import/github_controller.rb2
-rw-r--r--app/controllers/import/gitlab_controller.rb29
-rw-r--r--app/controllers/import/gitlab_groups_controller.rb65
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb27
-rw-r--r--app/controllers/projects/alert_management_controller.rb4
-rw-r--r--app/controllers/projects/alerting/notifications_controller.rb16
-rw-r--r--app/controllers/projects/badges_controller.rb11
-rw-r--r--app/controllers/projects/blame_controller.rb3
-rw-r--r--app/controllers/projects/blob_controller.rb6
-rw-r--r--app/controllers/projects/branches_controller.rb14
-rw-r--r--app/controllers/projects/ci/daily_build_group_report_results_controller.rb11
-rw-r--r--app/controllers/projects/discussions_controller.rb2
-rw-r--r--app/controllers/projects/environments_controller.rb2
-rw-r--r--app/controllers/projects/graphs_controller.rb10
-rw-r--r--app/controllers/projects/group_links_controller.rb7
-rw-r--r--app/controllers/projects/import/jira_controller.rb54
-rw-r--r--app/controllers/projects/issues_controller.rb1
-rw-r--r--app/controllers/projects/jobs_controller.rb40
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb11
-rw-r--r--app/controllers/projects/merge_requests/drafts_controller.rb129
-rw-r--r--app/controllers/projects/merge_requests_controller.rb3
-rw-r--r--app/controllers/projects/milestones_controller.rb4
-rw-r--r--app/controllers/projects/pipelines_controller.rb16
-rw-r--r--app/controllers/projects/refs_controller.rb2
-rw-r--r--app/controllers/projects/releases_controller.rb1
-rw-r--r--app/controllers/projects/services_controller.rb15
-rw-r--r--app/controllers/projects/settings/operations_controller.rb2
-rw-r--r--app/controllers/projects/snippets/application_controller.rb19
-rw-r--r--app/controllers/projects/snippets_controller.rb86
-rw-r--r--app/controllers/projects/tags_controller.rb18
-rw-r--r--app/controllers/projects/web_ide_terminals_controller.rb98
-rw-r--r--app/controllers/projects/wikis_controller.rb199
-rw-r--r--app/controllers/projects_controller.rb21
-rw-r--r--app/controllers/registrations/experience_levels_controller.rb44
-rw-r--r--app/controllers/registrations_controller.rb4
-rw-r--r--app/controllers/repositories/git_http_controller.rb2
-rw-r--r--app/controllers/search_controller.rb31
-rw-r--r--app/controllers/snippets/application_controller.rb22
-rw-r--r--app/controllers/snippets_controller.rb92
72 files changed, 1315 insertions, 656 deletions
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 709834a2bec..94c82c25357 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -12,6 +12,10 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
before_action :whitelist_query_limiting, only: [:usage_data]
+ before_action only: [:ci_cd] do
+ push_frontend_feature_flag(:ci_instance_variables_ui, default_enabled: true)
+ end
+
VALID_SETTING_PANELS = %w(general integrations repository
ci_cd reporting metrics_and_profiling
network preferences).freeze
@@ -216,6 +220,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
[
*::ApplicationSettingsHelper.visible_attributes,
*::ApplicationSettingsHelper.external_authorization_service_attributes,
+ *ApplicationSetting.repository_storages_weighted_attributes,
:lets_encrypt_notification_email,
:lets_encrypt_terms_of_service_accepted,
:domain_blacklist_file,
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index 4639d8adfe0..2449fa3128c 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -4,7 +4,7 @@ class Admin::RunnersController < Admin::ApplicationController
before_action :runner, except: [:index, :tag_list]
def index
- finder = Admin::RunnersFinder.new(params: params)
+ finder = Ci::RunnersFinder.new(current_user: current_user, params: params)
@runners = finder.execute
@active_runners_count = Ci::Runner.online.count
@sort = finder.sort_key
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index ee42baa8326..fc0acd8f99a 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -241,7 +241,8 @@ class Admin::UsersController < Admin::ApplicationController
:theme_id,
:twitter,
:username,
- :website_url
+ :website_url,
+ :note
]
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 54e3275662b..79a164a5574 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -12,6 +12,7 @@ class ApplicationController < ActionController::Base
include WorkhorseHelper
include EnforcesTwoFactorAuthentication
include WithPerformanceBar
+ include Gitlab::SearchContext::ControllerConcern
include SessionlessAuthentication
include SessionsHelper
include ConfirmEmailWarning
diff --git a/app/controllers/clusters/base_controller.rb b/app/controllers/clusters/base_controller.rb
index 8c13cc67be2..6b83400971d 100644
--- a/app/controllers/clusters/base_controller.rb
+++ b/app/controllers/clusters/base_controller.rb
@@ -7,7 +7,7 @@ class Clusters::BaseController < ApplicationController
before_action :authorize_read_cluster!
before_action do
- push_frontend_feature_flag(:managed_apps_local_tiller)
+ push_frontend_feature_flag(:managed_apps_local_tiller, clusterable)
end
helper_method :clusterable
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index aa39d430b24..46dec5f3287 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -23,6 +23,7 @@ class Clusters::ClustersController < Clusters::BaseController
respond_to do |format|
format.html
format.json do
+ Gitlab::PollingInterval.set_header(response, interval: STATUS_POLLING_INTERVAL)
serializer = ClusterSerializer.new(current_user: current_user)
render json: {
diff --git a/app/controllers/concerns/enforces_two_factor_authentication.rb b/app/controllers/concerns/enforces_two_factor_authentication.rb
index d486d734db8..6c443611a60 100644
--- a/app/controllers/concerns/enforces_two_factor_authentication.rb
+++ b/app/controllers/concerns/enforces_two_factor_authentication.rb
@@ -23,8 +23,7 @@ module EnforcesTwoFactorAuthentication
def two_factor_authentication_required?
Gitlab::CurrentSettings.require_two_factor_authentication? ||
- current_user.try(:require_two_factor_authentication_from_group?) ||
- current_user.try(:ultraauth_user?)
+ current_user.try(:require_two_factor_authentication_from_group?)
end
def current_user_requires_two_factor?
diff --git a/app/controllers/concerns/find_snippet.rb b/app/controllers/concerns/find_snippet.rb
new file mode 100644
index 00000000000..d51f1a1b3ad
--- /dev/null
+++ b/app/controllers/concerns/find_snippet.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module FindSnippet
+ extend ActiveSupport::Concern
+ include Gitlab::Utils::StrongMemoize
+
+ private
+
+ # rubocop:disable CodeReuse/ActiveRecord
+ def snippet
+ strong_memoize(:snippet) do
+ snippet_klass.inc_relations_for_view.find_by(id: snippet_id)
+ end
+ end
+ # rubocop:enable CodeReuse/ActiveRecord
+
+ def snippet_klass
+ raise NotImplementedError
+ end
+
+ def snippet_id
+ params[:id]
+ end
+end
diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb
index ff283f9bb62..cc9db7936e8 100644
--- a/app/controllers/concerns/integrations_actions.rb
+++ b/app/controllers/concerns/integrations_actions.rb
@@ -16,10 +16,12 @@ module IntegrationsActions
def update
saved = integration.update(service_params[:service])
+ overwrite = Gitlab::Utils.to_boolean(params[:overwrite])
respond_to do |format|
format.html do
if saved
+ PropagateIntegrationWorker.perform_async(integration.id, overwrite)
redirect_to scoped_edit_integration_path(integration), notice: success_message
else
render 'shared/integrations/edit'
@@ -34,6 +36,10 @@ module IntegrationsActions
end
end
+ def custom_integration_projects
+ Project.with_custom_integration_compared_to(integration).page(params[:page]).per(20)
+ end
+
def test
render json: {}, status: :ok
end
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index 0b1b3f2bcba..98fa8202e25 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -16,19 +16,6 @@ module IssuableActions
end
end
- def permitted_keys
- [
- :issuable_ids,
- :assignee_id,
- :milestone_id,
- :state_event,
- :subscription_event,
- label_ids: [],
- add_label_ids: [],
- remove_label_ids: []
- ]
- end
-
def show
respond_to do |format|
format.html do
@@ -221,10 +208,20 @@ module IssuableActions
end
def bulk_update_params
- permitted_keys_array = permitted_keys.dup
- permitted_keys_array << { assignee_ids: [] }
+ params.require(:update).permit(bulk_update_permitted_keys)
+ end
- params.require(:update).permit(permitted_keys_array)
+ def bulk_update_permitted_keys
+ [
+ :issuable_ids,
+ :assignee_id,
+ :milestone_id,
+ :state_event,
+ :subscription_event,
+ assignee_ids: [],
+ add_label_ids: [],
+ remove_label_ids: []
+ ]
end
def resource_name
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index 5aa00af8910..9ef067e8797 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -5,7 +5,6 @@ module IssuableCollections
include PaginatedCollection
include SortingHelper
include SortingPreference
- include Gitlab::IssuableMetadata
include Gitlab::Utils::StrongMemoize
included do
@@ -44,7 +43,7 @@ module IssuableCollections
def set_pagination
@issuables = @issuables.page(params[:page])
@issuables = per_page_for_relative_position if params[:sort] == 'relative_position'
- @issuable_meta_data = issuable_meta_data(@issuables, collection_type, current_user)
+ @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @issuables).data
@total_pages = issuable_page_count(@issuables)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb
index 78b3c6771b3..e3ac117660b 100644
--- a/app/controllers/concerns/issuable_collections_action.rb
+++ b/app/controllers/concerns/issuable_collections_action.rb
@@ -11,7 +11,7 @@ module IssuableCollectionsAction
.non_archived
.page(params[:page])
- @issuable_meta_data = issuable_meta_data(@issues, collection_type, current_user)
+ @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @issues).data
respond_to do |format|
format.html
@@ -22,7 +22,7 @@ module IssuableCollectionsAction
def merge_requests
@merge_requests = issuables_collection.page(params[:page])
- @issuable_meta_data = issuable_meta_data(@merge_requests, collection_type, current_user)
+ @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @merge_requests).data
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
diff --git a/app/controllers/concerns/known_sign_in.rb b/app/controllers/concerns/known_sign_in.rb
index 97883d8d08c..c0b9605de58 100644
--- a/app/controllers/concerns/known_sign_in.rb
+++ b/app/controllers/concerns/known_sign_in.rb
@@ -26,6 +26,6 @@ module KnownSignIn
end
def notify_user
- current_user.notification_service.unknown_sign_in(current_user, request.remote_ip)
+ current_user.notification_service.unknown_sign_in(current_user, request.remote_ip, current_user.current_sign_in_at)
end
end
diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb
index dbc575a1487..29138e7b014 100644
--- a/app/controllers/concerns/milestone_actions.rb
+++ b/app/controllers/concerns/milestone_actions.rb
@@ -51,13 +51,7 @@ module MilestoneActions
}
end
- # rubocop:disable Gitlab/ModuleWithInstanceVariables
def milestone_redirect_path
- if @milestone.global_milestone?
- url_for(action: :show, title: @milestone.title)
- else
- url_for(action: :show)
- end
+ url_for(action: :show)
end
- # rubocop:enable Gitlab/ModuleWithInstanceVariables
end
diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
index d4b0d3b2674..d3dfb1813e4 100644
--- a/app/controllers/concerns/notes_actions.rb
+++ b/app/controllers/concerns/notes_actions.rb
@@ -13,9 +13,7 @@ module NotesActions
end
def index
- current_fetched_at = Time.current.to_i
-
- notes_json = { notes: [], last_fetched_at: current_fetched_at }
+ notes_json = { notes: [], last_fetched_at: Time.current.to_i }
notes = notes_finder
.execute
@@ -24,7 +22,7 @@ module NotesActions
if notes_filter != UserPreference::NOTES_FILTERS[:only_comments]
notes =
ResourceEvents::MergeIntoNotesService
- .new(noteable, current_user, last_fetched_at: current_fetched_at)
+ .new(noteable, current_user, last_fetched_at: last_fetched_at)
.execute(notes)
end
diff --git a/app/controllers/concerns/preview_markdown.rb b/app/controllers/concerns/preview_markdown.rb
index ba15d611c0d..2916762e31f 100644
--- a/app/controllers/concerns/preview_markdown.rb
+++ b/app/controllers/concerns/preview_markdown.rb
@@ -32,7 +32,7 @@ module PreviewMarkdown
def markdown_context_params
case controller_name
- when 'wikis' then { pipeline: :wiki, project_wiki: @project_wiki, page_slug: params[:id] }
+ when 'wikis' then { pipeline: :wiki, wiki: wiki, page_slug: params[:id] }
when 'snippets' then { skip_project_check: true }
when 'groups' then { group: group }
when 'projects' then projects_filter_params
diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb
index e2c83f9a069..e78fa8f8250 100644
--- a/app/controllers/concerns/service_params.rb
+++ b/app/controllers/concerns/service_params.rb
@@ -6,6 +6,7 @@ module ServiceParams
ALLOWED_PARAMS_CE = [
:active,
:add_pusher,
+ :alert_events,
:api_key,
:api_url,
:api_version,
@@ -28,6 +29,8 @@ module ServiceParams
:drone_url,
:enable_ssl_verification,
:external_wiki_url,
+ :google_iap_service_account_json,
+ :google_iap_audience_client_id,
# 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
diff --git a/app/controllers/concerns/snippet_authorizations.rb b/app/controllers/concerns/snippet_authorizations.rb
new file mode 100644
index 00000000000..9bbb0cc6faa
--- /dev/null
+++ b/app/controllers/concerns/snippet_authorizations.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module SnippetAuthorizations
+ extend ActiveSupport::Concern
+
+ private
+
+ def authorize_read_snippet!
+ return render_404 unless can?(current_user, :read_snippet, snippet)
+ end
+
+ def authorize_update_snippet!
+ return render_404 unless can?(current_user, :update_snippet, snippet)
+ end
+
+ def authorize_admin_snippet!
+ return render_404 unless can?(current_user, :admin_snippet, snippet)
+ end
+
+ def authorize_create_snippet!
+ return render_404 unless can?(current_user, :create_snippet)
+ end
+end
diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb
index e78723bdda2..51fc12398d9 100644
--- a/app/controllers/concerns/snippets_actions.rb
+++ b/app/controllers/concerns/snippets_actions.rb
@@ -3,9 +3,18 @@
module SnippetsActions
extend ActiveSupport::Concern
include SendsBlob
+ include RendersNotes
+ include RendersBlob
+ include PaginatedCollection
+ include Gitlab::NoteableMetadata
included do
+ skip_before_action :verify_authenticity_token,
+ if: -> { action_name == 'show' && js_request? }
+
before_action :redirect_if_binary, only: [:edit, :update]
+
+ respond_to :html
end
def edit
@@ -43,6 +52,58 @@ module SnippetsActions
request.format.js?
end
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def show
+ conditionally_expand_blob(blob)
+
+ respond_to do |format|
+ format.html do
+ @note = Note.new(noteable: @snippet, project: @snippet.project)
+ @noteable = @snippet
+
+ @discussions = @snippet.discussions
+ @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
+ render 'show'
+ end
+
+ format.json do
+ render_blob_json(blob)
+ end
+
+ format.js do
+ if @snippet.embeddable?
+ render 'shared/snippets/show'
+ else
+ head :not_found
+ end
+ end
+ end
+ end
+
+ def update
+ update_params = snippet_params.merge(spammable_params)
+
+ service_response = Snippets::UpdateService.new(@snippet.project, current_user, update_params).execute(@snippet)
+ @snippet = service_response.payload[:snippet]
+
+ handle_repository_error(:edit)
+ end
+
+ def destroy
+ service_response = Snippets::DestroyService.new(current_user, @snippet).execute
+
+ if service_response.success?
+ redirect_to gitlab_dashboard_snippets_path(@snippet), status: :found
+ elsif service_response.http_status == 403
+ access_denied!
+ else
+ redirect_to gitlab_snippet_path(@snippet),
+ status: :found,
+ alert: service_response.message
+ end
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
private
def content_disposition
diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb
new file mode 100644
index 00000000000..b4b4fd84c37
--- /dev/null
+++ b/app/controllers/concerns/wiki_actions.rb
@@ -0,0 +1,232 @@
+# frozen_string_literal: true
+
+module WikiActions
+ include SendsBlob
+ include Gitlab::Utils::StrongMemoize
+ extend ActiveSupport::Concern
+
+ included do
+ before_action :authorize_read_wiki!
+ before_action :authorize_create_wiki!, only: [:edit, :create]
+ before_action :authorize_admin_wiki!, only: :destroy
+
+ before_action :wiki
+ before_action :page, only: [:show, :edit, :update, :history, :destroy]
+ before_action :load_sidebar, except: [:pages]
+
+ before_action only: [:show, :edit, :update] do
+ @valid_encoding = valid_encoding?
+ end
+
+ before_action only: [:edit, :update], unless: :valid_encoding? do
+ redirect_to wiki_page_path(wiki, page)
+ end
+ end
+
+ def new
+ redirect_to wiki_page_path(wiki, SecureRandom.uuid, random_title: true)
+ end
+
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def pages
+ @wiki_pages = Kaminari.paginate_array(
+ wiki.list_pages(sort: params[:sort], direction: params[:direction])
+ ).page(params[:page])
+
+ @wiki_entries = WikiPage.group_by_directory(@wiki_pages)
+
+ render 'shared/wikis/pages'
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ # `#show` handles a number of scenarios:
+ #
+ # - If `id` matches a WikiPage, then show the wiki page.
+ # - If `id` is a file in the wiki repository, then send the file.
+ # - If we know the user wants to create a new page with the given `id`,
+ # then display a create form.
+ # - Otherwise show the empty wiki page and invite the user to create a page.
+ #
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def show
+ if page
+ set_encoding_error unless valid_encoding?
+
+ # Assign vars expected by MarkupHelper
+ @ref = params[:version_id]
+ @path = page.path
+
+ render 'shared/wikis/show'
+ elsif file_blob
+ send_blob(wiki.repository, file_blob, allow_caching: container.public?)
+ elsif show_create_form?
+ # Assign a title to the WikiPage unless `id` is a randomly generated slug from #new
+ title = params[:id] unless params[:random_title].present?
+
+ @page = build_page(title: title)
+
+ render 'shared/wikis/edit'
+ else
+ render 'shared/wikis/empty'
+ end
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ def edit
+ render 'shared/wikis/edit'
+ end
+
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def update
+ return render('shared/wikis/empty') unless can?(current_user, :create_wiki, container)
+
+ @page = WikiPages::UpdateService.new(container: container, current_user: current_user, params: wiki_params).execute(page)
+
+ if page.valid?
+ redirect_to(
+ wiki_page_path(wiki, page),
+ notice: _('Wiki was successfully updated.')
+ )
+ else
+ render 'shared/wikis/edit'
+ end
+ rescue WikiPage::PageChangedError, WikiPage::PageRenameError, Gitlab::Git::Wiki::OperationError => e
+ @error = e
+ render 'shared/wikis/edit'
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def create
+ @page = WikiPages::CreateService.new(container: container, current_user: current_user, params: wiki_params).execute
+
+ if page.persisted?
+ redirect_to(
+ wiki_page_path(wiki, page),
+ notice: _('Wiki was successfully updated.')
+ )
+ else
+ render 'shared/wikis/edit'
+ end
+ rescue Gitlab::Git::Wiki::OperationError => e
+ @page = build_page(wiki_params)
+ @error = e
+ render 'shared/wikis/edit'
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def history
+ if page
+ @page_versions = Kaminari.paginate_array(page.versions(page: params[:page].to_i),
+ total_count: page.count_versions)
+ .page(params[:page])
+
+ render 'shared/wikis/history'
+ else
+ redirect_to(
+ wiki_path(wiki),
+ notice: _("Page not found")
+ )
+ end
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def destroy
+ WikiPages::DestroyService.new(container: container, current_user: current_user).execute(page)
+
+ redirect_to wiki_path(wiki),
+ status: :found,
+ notice: _("Page was successfully deleted")
+ rescue Gitlab::Git::Wiki::OperationError => e
+ @error = e
+ render 'shared/wikis/edit'
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ private
+
+ def container
+ raise NotImplementedError
+ end
+
+ def show_create_form?
+ can?(current_user, :create_wiki, container) &&
+ page.nil? &&
+ # Always show the create form when the wiki has had at least one page created.
+ # Otherwise, we only show the form when the user has navigated from
+ # the 'empty wiki' page
+ (wiki.exists? || params[:view] == 'create')
+ end
+
+ def wiki
+ strong_memoize(:wiki) do
+ wiki = Wiki.for_container(container, current_user)
+
+ # Call #wiki to make sure the Wiki Repo is initialized
+ wiki.wiki
+
+ wiki
+ end
+ rescue Wiki::CouldNotCreateWikiError
+ flash[:notice] = _("Could not create Wiki Repository at this time. Please try again later.")
+ redirect_to container
+ false
+ end
+
+ def page
+ strong_memoize(:page) do
+ wiki.find_page(*page_params)
+ end
+ end
+
+ # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ def load_sidebar
+ @sidebar_page = wiki.find_sidebar(params[:version_id])
+
+ unless @sidebar_page # Fallback to default sidebar
+ @sidebar_wiki_entries, @sidebar_limited = wiki.sidebar_entries
+ end
+ end
+ # rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ def wiki_params
+ params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha)
+ end
+
+ def build_page(args = {})
+ WikiPage.new(wiki).tap do |page|
+ page.update_attributes(args) # rubocop:disable Rails/ActiveRecordAliases
+ end
+ end
+
+ def page_params
+ keys = [:id]
+ keys << :version_id if params[:action] == 'show'
+
+ params.values_at(*keys)
+ end
+
+ def valid_encoding?
+ page_encoding == Encoding::UTF_8
+ end
+
+ def page_encoding
+ strong_memoize(:page_encoding) { page&.content&.encoding }
+ end
+
+ def set_encoding_error
+ flash.now[:notice] = _("The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.")
+ end
+
+ def file_blob
+ strong_memoize(:file_blob) do
+ commit = wiki.repository.commit(wiki.default_branch)
+
+ next unless commit
+
+ wiki.repository.blob_at(commit.id, params[:id])
+ end
+ end
+end
diff --git a/app/controllers/concerns/workhorse_import_export_upload.rb b/app/controllers/concerns/workhorse_import_export_upload.rb
new file mode 100644
index 00000000000..3c52f4d7adf
--- /dev/null
+++ b/app/controllers/concerns/workhorse_import_export_upload.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module WorkhorseImportExportUpload
+ extend ActiveSupport::Concern
+ include WorkhorseRequest
+
+ included do
+ skip_before_action :verify_authenticity_token, only: %i[authorize]
+ before_action :verify_workhorse_api!, only: %i[authorize]
+ end
+
+ def authorize
+ set_workhorse_internal_api_content_type
+
+ authorized = ImportExportUploader.workhorse_authorize(
+ has_length: false,
+ maximum_size: Gitlab::CurrentSettings.max_import_size.megabytes
+ )
+
+ render json: authorized
+ rescue SocketError
+ render json: _("Error uploading file"), status: :internal_server_error
+ end
+
+ private
+
+ def file_is_valid?(file)
+ return false unless file.is_a?(::UploadedFile)
+
+ ImportExportUploader::EXTENSION_WHITELIST
+ .include?(File.extname(file.original_filename).delete('.'))
+ end
+end
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index d34a07324da..14f9a026688 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -1,48 +1,32 @@
# frozen_string_literal: true
class Dashboard::MilestonesController < Dashboard::ApplicationController
- include MilestoneActions
-
before_action :projects
before_action :groups, only: :index
- before_action :milestone, only: [:show, :merge_requests, :participants, :labels]
def index
respond_to do |format|
format.html do
- @milestone_states = Milestone.states_count(@projects.select(:id), @groups.select(:id))
- @milestones = Kaminari.paginate_array(milestones).page(params[:page])
+ @milestone_states = Milestone.states_count(@projects.select(:id), groups.select(:id))
+ @milestones = milestones.page(params[:page])
end
format.json do
- render json: milestones
+ render json: milestones.to_json(only: [:id, :title], methods: :name)
end
end
end
- def show
- end
-
private
- def group_milestones
- DashboardGroupMilestone.build_collection(groups, params)
- end
-
- # See [#39545](https://gitlab.com/gitlab-org/gitlab-foss/issues/39545) for info about the deprecation of dynamic milestones
- def dynamic_milestones
- DashboardMilestone.build_collection(@projects, params)
- end
-
def milestones
- @milestones = group_milestones + dynamic_milestones
- end
-
- def milestone
- @milestone = DashboardMilestone.build(@projects, params[:title])
- render_404 unless @milestone
+ MilestonesFinder.new(search_params).execute
end
def groups
@groups ||= GroupsFinder.new(current_user, all_available: false).execute
end
+
+ def search_params
+ params.permit(:state, :search_title).merge(group_ids: groups, project_ids: projects)
+ end
end
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index ebee8e9094e..8a8064b24c2 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -17,7 +17,9 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end
def destroy
- TodoService.new.mark_todos_as_done_by_ids(params[:id], current_user)
+ todo = current_user.todos.find(params[:id])
+
+ TodoService.new.resolve_todo(todo, current_user, resolved_by_action: :mark_done)
respond_to do |format|
format.html do
@@ -31,7 +33,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end
def destroy_all
- updated_ids = TodoService.new.mark_todos_as_done(@todos, current_user)
+ updated_ids = TodoService.new.resolve_todos(@todos, current_user, resolved_by_action: :mark_all_done)
respond_to do |format|
format.html { redirect_to dashboard_todos_path, status: :found, notice: _('Everything on your to-do list is marked as done.') }
@@ -41,13 +43,13 @@ class Dashboard::TodosController < Dashboard::ApplicationController
end
def restore
- TodoService.new.mark_todos_as_pending_by_ids(params[:id], current_user)
+ TodoService.new.restore_todo(current_user.todos.find(params[:id]), current_user)
render json: todos_counts
end
def bulk_restore
- TodoService.new.mark_todos_as_pending_by_ids(params[:ids], current_user)
+ TodoService.new.restore_todos(current_user.todos.for_ids(params[:ids]), current_user)
render json: todos_counts
end
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index a9bd24890ee..c618ee8566a 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -9,6 +9,7 @@ class Groups::BoardsController < Groups::ApplicationController
before_action do
push_frontend_feature_flag(:multi_select_board, default_enabled: true)
push_frontend_feature_flag(:sfc_issue_boards, default_enabled: true)
+ push_frontend_feature_flag(:boards_with_swimlanes, group, default_enabled: false)
end
private
diff --git a/app/controllers/groups/group_links_controller.rb b/app/controllers/groups/group_links_controller.rb
index 52ee69edaa5..c395b93f4e7 100644
--- a/app/controllers/groups/group_links_controller.rb
+++ b/app/controllers/groups/group_links_controller.rb
@@ -27,7 +27,7 @@ class Groups::GroupLinksController < Groups::ApplicationController
end
def destroy
- Groups::GroupLinks::DestroyService.new(nil, nil).execute(@group_link)
+ Groups::GroupLinks::DestroyService.new(group, current_user).execute(@group_link)
respond_to do |format|
format.html do
diff --git a/app/controllers/groups/imports_controller.rb b/app/controllers/groups/imports_controller.rb
new file mode 100644
index 00000000000..b611685f9bc
--- /dev/null
+++ b/app/controllers/groups/imports_controller.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class Groups::ImportsController < Groups::ApplicationController
+ include ContinueParams
+
+ def show
+ if @group.import_state.nil? || @group.import_state.finished?
+ if continue_params[:to]
+ redirect_to continue_params[:to], notice: continue_params[:notice]
+ else
+ redirect_to group_path(@group), notice: s_('GroupImport|The group was successfully imported.')
+ end
+ elsif @group.import_state.failed?
+ redirect_to new_group_path(@group), alert: s_('GroupImport|Failed to import group.')
+ else
+ flash.now[:notice] = continue_params[:notice_now]
+ end
+ end
+end
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 8cfbd293597..df3fb6b67c2 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -6,17 +6,17 @@ class Groups::MilestonesController < Groups::ApplicationController
before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels, :destroy]
before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy]
before_action do
- push_frontend_feature_flag(:burnup_charts)
+ push_frontend_feature_flag(:burnup_charts, @group)
end
def index
respond_to do |format|
format.html do
- @milestone_states = Milestone.states_count(group_projects_with_access, [group])
- @milestones = Kaminari.paginate_array(milestones).page(params[:page])
+ @milestone_states = Milestone.states_count(group_projects_with_access.without_order, [group])
+ @milestones = milestones.page(params[:page])
end
format.json do
- render json: milestones.map { |m| m.for_display.slice(:id, :title, :name) }
+ render json: milestones.to_json(only: [:id, :title], methods: :name)
end
end
end
@@ -29,7 +29,7 @@ class Groups::MilestonesController < Groups::ApplicationController
@milestone = Milestones::CreateService.new(group, current_user, milestone_params).execute
if @milestone.persisted?
- redirect_to milestone_path
+ redirect_to milestone_path(@milestone)
else
render "new"
end
@@ -39,23 +39,15 @@ class Groups::MilestonesController < Groups::ApplicationController
end
def edit
- render_404 if @milestone.legacy_group_milestone?
end
def update
- # Keep this compatible with legacy group milestones where we have to update
- # all projects milestones states at once.
- milestones, update_params = get_milestones_for_update
- milestones.each do |milestone|
- Milestones::UpdateService.new(milestone.resource_parent, current_user, update_params).execute(milestone)
- end
+ Milestones::UpdateService.new(@milestone.parent, current_user, milestone_params).execute(@milestone)
- redirect_to milestone_path
+ redirect_to milestone_path(@milestone)
end
def destroy
- return render_404 if @milestone.legacy_group_milestone?
-
Milestones::DestroyService.new(group, current_user).execute(@milestone)
respond_to do |format|
@@ -66,14 +58,6 @@ class Groups::MilestonesController < Groups::ApplicationController
private
- def get_milestones_for_update
- if @milestone.legacy_group_milestone?
- [@milestone.milestones, legacy_milestone_params]
- else
- [[@milestone], milestone_params]
- end
- end
-
def authorize_admin_milestones!
return render_404 unless can?(current_user, :admin_milestone, group)
end
@@ -82,27 +66,21 @@ class Groups::MilestonesController < Groups::ApplicationController
params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
end
- def legacy_milestone_params
- params.require(:milestone).permit(:state_event)
+ def milestones
+ MilestonesFinder.new(search_params).execute
end
- def milestone_path
- if @milestone.legacy_group_milestone?
- group_milestone_path(group, @milestone.safe_title, title: @milestone.title)
- else
- group_milestone_path(group, @milestone.iid)
- end
+ def milestone
+ @milestone = group.milestones.find_by_iid(params[:id])
+
+ render_404 unless @milestone
end
- def milestones
- milestones = MilestonesFinder.new(search_params).execute
+ def search_params
+ groups = request.format.json? ? group_ids(include_ancestors: true) : group_ids
@sort = params[:sort] || 'due_date_asc'
- MilestoneArray.sort(milestones + legacy_milestones, @sort)
- end
-
- def legacy_milestones
- GroupMilestone.build_collection(group, group_projects_with_access, params)
+ params.permit(:state, :search_title).merge(sort: @sort, group_ids: groups, project_ids: group_projects_with_access)
end
def group_projects_with_access
@@ -116,23 +94,6 @@ class Groups::MilestonesController < Groups::ApplicationController
group.self_and_descendants.public_or_visible_to_user(current_user).select(:id)
end
end
-
- def milestone
- @milestone =
- if params[:title]
- GroupMilestone.build(group, group_projects_with_access, params[:title])
- else
- group.milestones.find_by_iid(params[:id])
- end
-
- render_404 unless @milestone
- end
-
- def search_params
- groups = request.format.json? ? group_ids(include_ancestors: true) : group_ids
-
- params.permit(:state, :search_title).merge(group_ids: groups)
- end
end
Groups::MilestonesController.prepend_if_ee('EE::Groups::MilestonesController')
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index d5f2239b16a..fba374dbb44 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -57,6 +57,8 @@ class GroupsController < Groups::ApplicationController
@group = Groups::CreateService.new(current_user, group_params).execute
if @group.persisted?
+ track_experiment_event(:onboarding_issues, 'created_namespace')
+
notice = if @group.chat_team.present?
"Group '#{@group.name}' and its Mattermost team were successfully created."
else
@@ -72,7 +74,11 @@ class GroupsController < Groups::ApplicationController
def show
respond_to do |format|
format.html do
- render_show_html
+ if @group.import_state&.in_progress?
+ redirect_to group_import_path(@group)
+ else
+ render_show_html
+ end
end
format.atom do
@@ -264,11 +270,12 @@ class GroupsController < Groups::ApplicationController
def export_rate_limit
prefixed_action = "group_#{params[:action]}".to_sym
- if Gitlab::ApplicationRateLimiter.throttled?(prefixed_action, scope: [current_user, prefixed_action, @group])
+ scope = params[:action] == :download_export ? @group : nil
+
+ if Gitlab::ApplicationRateLimiter.throttled?(prefixed_action, scope: [current_user, scope].compact)
Gitlab::ApplicationRateLimiter.log_request(request, "#{prefixed_action}_request_limit".to_sym, current_user)
- flash[:alert] = _('This endpoint has been requested too many times. Try again later.')
- redirect_to edit_group_path(@group)
+ render plain: _('This endpoint has been requested too many times. Try again later.'), status: :too_many_requests
end
end
diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb
index 8a838db04f9..2bf7bdd1ae0 100644
--- a/app/controllers/ide_controller.rb
+++ b/app/controllers/ide_controller.rb
@@ -6,9 +6,11 @@ class IdeController < ApplicationController
include ClientsidePreviewCSP
include StaticObjectExternalStorageCSP
+ before_action do
+ push_frontend_feature_flag(:build_service_proxy)
+ end
+
def index
Gitlab::UsageDataCounters::WebIdeCounter.increment_views_count
end
end
-
-IdeController.prepend_if_ee('EE::IdeController')
diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb
index 04919a4b9d0..afdea4f7c9d 100644
--- a/app/controllers/import/base_controller.rb
+++ b/app/controllers/import/base_controller.rb
@@ -1,10 +1,86 @@
# frozen_string_literal: true
class Import::BaseController < ApplicationController
+ include ActionView::Helpers::SanitizeHelper
+
before_action :import_rate_limit, only: [:create]
+ def status
+ respond_to do |format|
+ format.json do
+ render json: { imported_projects: serialized_imported_projects,
+ provider_repos: serialized_provider_repos,
+ incompatible_repos: serialized_incompatible_repos,
+ namespaces: serialized_namespaces }
+ end
+ format.html
+ end
+ end
+
+ def realtime_changes
+ Gitlab::PollingInterval.set_header(response, interval: 3_000)
+
+ render json: already_added_projects.to_json(only: [:id], methods: [:import_status])
+ end
+
+ protected
+
+ def importable_repos
+ raise NotImplementedError
+ end
+
+ def incompatible_repos
+ []
+ end
+
+ def provider_name
+ raise NotImplementedError
+ end
+
+ def provider_url
+ raise NotImplementedError
+ end
+
private
+ def filter_attribute
+ :name
+ end
+
+ def sanitized_filter_param
+ @filter ||= sanitize(params[:filter])
+ end
+
+ def filtered(collection)
+ return collection unless sanitized_filter_param
+
+ collection.select { |item| item[filter_attribute].include?(sanitized_filter_param) }
+ end
+
+ def serialized_provider_repos
+ Import::ProviderRepoSerializer.new(current_user: current_user).represent(importable_repos, provider: provider_name, provider_url: provider_url)
+ end
+
+ def serialized_incompatible_repos
+ Import::ProviderRepoSerializer.new(current_user: current_user).represent(incompatible_repos, provider: provider_name, provider_url: provider_url)
+ end
+
+ def serialized_imported_projects
+ ProjectSerializer.new.represent(already_added_projects, serializer: :import, provider_url: provider_url)
+ end
+
+ def already_added_projects
+ @already_added_projects ||= filtered(find_already_added_projects(provider_name))
+ end
+
+ def serialized_namespaces
+ NamespaceSerializer.new.represent(namespaces)
+ end
+
+ def namespaces
+ current_user.manageable_groups_with_routes
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def find_already_added_projects(import_type)
current_user.created_projects.where(import_type: import_type).with_import_state
diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb
index c37e799de62..4886aeb5e3f 100644
--- a/app/controllers/import/bitbucket_controller.rb
+++ b/app/controllers/import/bitbucket_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Import::BitbucketController < Import::BaseController
+ extend ::Gitlab::Utils::Override
+
include ActionView::Helpers::SanitizeHelper
before_action :verify_bitbucket_import_enabled
@@ -10,7 +12,7 @@ class Import::BitbucketController < Import::BaseController
rescue_from Bitbucket::Error::Unauthorized, with: :bitbucket_unauthorized
def callback
- response = client.auth_code.get_token(params[:code], redirect_uri: users_import_bitbucket_callback_url)
+ response = oauth_client.auth_code.get_token(params[:code], redirect_uri: users_import_bitbucket_callback_url)
session[:bitbucket_token] = response.token
session[:bitbucket_expires_at] = response.expires_at
@@ -22,9 +24,10 @@ class Import::BitbucketController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord
def status
+ return super if Feature.enabled?(:new_import_ui)
+
bitbucket_client = Bitbucket::Client.new(credentials)
repos = bitbucket_client.repos(filter: sanitized_filter_param)
-
@repos, @incompatible_repos = repos.partition { |repo| repo.valid? }
@already_added_projects = find_already_added_projects('bitbucket')
@@ -38,6 +41,10 @@ class Import::BitbucketController < Import::BaseController
render json: find_jobs('bitbucket')
end
+ def realtime_changes
+ super
+ end
+
def create
bitbucket_client = Bitbucket::Client.new(credentials)
@@ -59,7 +66,7 @@ class Import::BitbucketController < Import::BaseController
project = Gitlab::BitbucketImport::ProjectCreator.new(repo, project_name, target_namespace, current_user, credentials).execute
if project.persisted?
- render json: ProjectSerializer.new.represent(project)
+ render json: ProjectSerializer.new.represent(project, serializer: :import)
else
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
end
@@ -68,16 +75,50 @@ class Import::BitbucketController < Import::BaseController
end
end
+ protected
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ override :importable_repos
+ def importable_repos
+ already_added_projects_names = already_added_projects.map(&:import_source)
+
+ bitbucket_repos.reject { |repo| already_added_projects_names.include?(repo.full_name) || !repo.valid? }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ override :incompatible_repos
+ def incompatible_repos
+ bitbucket_repos.reject { |repo| repo.valid? }
+ end
+
+ override :provider_name
+ def provider_name
+ :bitbucket
+ end
+
+ override :provider_url
+ def provider_url
+ provider.url
+ end
+
private
- def client
- @client ||= OAuth2::Client.new(provider.app_id, provider.app_secret, options)
+ def oauth_client
+ @oauth_client ||= OAuth2::Client.new(provider.app_id, provider.app_secret, options)
end
def provider
Gitlab::Auth::OAuth::Provider.config_for('bitbucket')
end
+ def client
+ @client ||= Bitbucket::Client.new(credentials)
+ end
+
+ def bitbucket_repos
+ @bitbucket_repos ||= client.repos(filter: sanitized_filter_param).to_a
+ end
+
def options
OmniAuth::Strategies::Bitbucket.default_options[:client_options].deep_symbolize_keys
end
@@ -91,7 +132,7 @@ class Import::BitbucketController < Import::BaseController
end
def go_to_bitbucket_for_permissions
- redirect_to client.auth_code.authorize_url(redirect_uri: users_import_bitbucket_callback_url)
+ redirect_to oauth_client.auth_code.authorize_url(redirect_uri: users_import_bitbucket_callback_url)
end
def bitbucket_unauthorized
diff --git a/app/controllers/import/bitbucket_server_controller.rb b/app/controllers/import/bitbucket_server_controller.rb
index 5fb7b5dccc5..9aa8110257d 100644
--- a/app/controllers/import/bitbucket_server_controller.rb
+++ b/app/controllers/import/bitbucket_server_controller.rb
@@ -1,12 +1,16 @@
# frozen_string_literal: true
class Import::BitbucketServerController < Import::BaseController
+ extend ::Gitlab::Utils::Override
+
include ActionView::Helpers::SanitizeHelper
before_action :verify_bitbucket_server_import_enabled
before_action :bitbucket_auth, except: [:new, :configure]
before_action :validate_import_params, only: [:create]
+ rescue_from BitbucketServer::Connection::ConnectionError, with: :bitbucket_connection_error
+
# As a basic sanity check to prevent URL injection, restrict project
# repository input and repository slugs to allowed characters. For Bitbucket:
#
@@ -24,7 +28,7 @@ class Import::BitbucketServerController < Import::BaseController
end
def create
- repo = bitbucket_client.repo(@project_key, @repo_slug)
+ repo = client.repo(@project_key, @repo_slug)
unless repo
return render json: { errors: _("Project %{project_repo} could not be found") % { project_repo: "#{@project_key}/#{@repo_slug}" } }, status: :unprocessable_entity
@@ -38,15 +42,13 @@ class Import::BitbucketServerController < Import::BaseController
project = Gitlab::BitbucketServerImport::ProjectCreator.new(@project_key, @repo_slug, repo, project_name, target_namespace, current_user, credentials).execute
if project.persisted?
- render json: ProjectSerializer.new.represent(project)
+ render json: ProjectSerializer.new.represent(project, serializer: :import)
else
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
end
else
render json: { errors: _('This namespace has already been taken! Please choose another one.') }, status: :unprocessable_entity
end
- rescue BitbucketServer::Connection::ConnectionError => error
- render json: { errors: _("Unable to connect to server: %{error}") % { error: error } }, status: :unprocessable_entity
end
def configure
@@ -59,7 +61,9 @@ class Import::BitbucketServerController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord
def status
- @collection = bitbucket_client.repos(page_offset: page_offset, limit: limit_per_page, filter: sanitized_filter_param)
+ return super if Feature.enabled?(:new_import_ui)
+
+ @collection = client.repos(page_offset: page_offset, limit: limit_per_page, filter: sanitized_filter_param)
@repos, @incompatible_repos = @collection.partition { |repo| repo.valid? }
# Use the import URL to filter beyond what BaseService#find_already_added_projects
@@ -67,10 +71,6 @@ class Import::BitbucketServerController < Import::BaseController
already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos.reject! { |repo| already_added_projects_names.include?(repo.browse_url) }
- rescue BitbucketServer::Connection::ConnectionError => error
- flash[:alert] = _("Unable to connect to server: %{error}") % { error: error }
- clear_session_data
- redirect_to new_import_bitbucket_server_path
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -78,6 +78,38 @@ class Import::BitbucketServerController < Import::BaseController
render json: find_jobs('bitbucket_server')
end
+ def realtime_changes
+ super
+ end
+
+ protected
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ override :importable_repos
+ def importable_repos
+ # Use the import URL to filter beyond what BaseService#find_already_added_projects
+ already_added_projects = filter_added_projects('bitbucket_server', bitbucket_repos.map(&:browse_url))
+ already_added_projects_names = already_added_projects.map(&:import_source)
+
+ bitbucket_repos.reject { |repo| already_added_projects_names.include?(repo.browse_url) || !repo.valid? }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ override :incompatible_repos
+ def incompatible_repos
+ bitbucket_repos.reject { |repo| repo.valid? }
+ end
+
+ override :provider_name
+ def provider_name
+ :bitbucket_server
+ end
+
+ override :provider_url
+ def provider_url
+ session[bitbucket_server_url_key]
+ end
+
private
# rubocop: disable CodeReuse/ActiveRecord
@@ -86,8 +118,12 @@ class Import::BitbucketServerController < Import::BaseController
end
# rubocop: enable CodeReuse/ActiveRecord
- def bitbucket_client
- @bitbucket_client ||= BitbucketServer::Client.new(credentials)
+ def client
+ @client ||= BitbucketServer::Client.new(credentials)
+ end
+
+ def bitbucket_repos
+ @bitbucket_repos ||= client.repos(page_offset: page_offset, limit: limit_per_page, filter: sanitized_filter_param).to_a
end
def validate_import_params
@@ -153,4 +189,23 @@ class Import::BitbucketServerController < Import::BaseController
def sanitized_filter_param
sanitize(params[:filter])
end
+
+ def bitbucket_connection_error(error)
+ flash[:alert] = _("Unable to connect to server: %{error}") % { error: error }
+ clear_session_data
+
+ respond_to do |format|
+ format.json do
+ render json: {
+ error: {
+ message: _("Unable to connect to server: %{error}") % { error: error },
+ redirect: new_import_bitbucket_server_path
+ }
+ }, status: :unprocessable_entity
+ end
+ format.html do
+ redirect_to new_import_bitbucket_server_path
+ end
+ end
+ end
end
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index 4fb6efde7ff..91779a5d6cc 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Import::FogbugzController < Import::BaseController
+ extend ::Gitlab::Utils::Override
+
before_action :verify_fogbugz_import_enabled
before_action :user_map, only: [:new_user_map, :create_user_map]
before_action :verify_blocked_uri, only: :callback
@@ -48,6 +50,8 @@ class Import::FogbugzController < Import::BaseController
return redirect_to new_import_fogbugz_path
end
+ return super if Feature.enabled?(:new_import_ui)
+
@repos = client.repos
@already_added_projects = find_already_added_projects('fogbugz')
@@ -57,6 +61,10 @@ class Import::FogbugzController < Import::BaseController
end
# rubocop: enable CodeReuse/ActiveRecord
+ def realtime_changes
+ super
+ end
+
def jobs
render json: find_jobs('fogbugz')
end
@@ -69,12 +77,35 @@ class Import::FogbugzController < Import::BaseController
project = Gitlab::FogbugzImport::ProjectCreator.new(repo, fb_session, current_user.namespace, current_user, umap).execute
if project.persisted?
- render json: ProjectSerializer.new.represent(project)
+ render json: ProjectSerializer.new.represent(project, serializer: :import)
else
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
end
end
+ protected
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ override :importable_repos
+ def importable_repos
+ repos = client.repos
+
+ already_added_projects_names = already_added_projects.map(&:import_source)
+
+ repos.reject { |repo| already_added_projects_names.include? repo.name }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ override :provider_name
+ def provider_name
+ :fogbugz
+ end
+
+ override :provider_url
+ def provider_url
+ session[:fogbugz_uri]
+ end
+
private
def client
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index 4e8ceae75bd..097edcd6075 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -76,7 +76,7 @@ class Import::GithubController < Import::BaseController
def serialized_provider_repos
repos = client_repos.reject { |repo| already_added_project_names.include? repo.full_name }
- ProviderRepoSerializer.new(current_user: current_user).represent(repos, provider: provider, provider_url: provider_url)
+ Import::ProviderRepoSerializer.new(current_user: current_user).represent(repos, provider: provider, provider_url: provider_url)
end
def serialized_namespaces
diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb
index 5ec8e9e6fc5..a95a67e208c 100644
--- a/app/controllers/import/gitlab_controller.rb
+++ b/app/controllers/import/gitlab_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Import::GitlabController < Import::BaseController
+ extend ::Gitlab::Utils::Override
+
MAX_PROJECT_PAGES = 15
PER_PAGE_PROJECTS = 100
@@ -16,6 +18,8 @@ class Import::GitlabController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord
def status
+ return super if Feature.enabled?(:new_import_ui)
+
@repos = client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS)
@already_added_projects = find_already_added_projects('gitlab')
@@ -37,7 +41,7 @@ class Import::GitlabController < Import::BaseController
project = Gitlab::GitlabImport::ProjectCreator.new(repo, target_namespace, current_user, access_params).execute
if project.persisted?
- render json: ProjectSerializer.new.represent(project)
+ render json: ProjectSerializer.new.represent(project, serializer: :import)
else
render json: { errors: project_save_error(project) }, status: :unprocessable_entity
end
@@ -46,6 +50,29 @@ class Import::GitlabController < Import::BaseController
end
end
+ protected
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ override :importable_repos
+ def importable_repos
+ repos = client.projects(starting_page: 1, page_limit: MAX_PROJECT_PAGES, per_page: PER_PAGE_PROJECTS)
+
+ already_added_projects_names = already_added_projects.map(&:import_source)
+
+ repos.reject { |repo| already_added_projects_names.include? repo["path_with_namespace"] }
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ override :provider_name
+ def provider_name
+ :gitlab
+ end
+
+ override :provider_url
+ def provider_url
+ 'https://gitlab.com'
+ end
+
private
def client
diff --git a/app/controllers/import/gitlab_groups_controller.rb b/app/controllers/import/gitlab_groups_controller.rb
new file mode 100644
index 00000000000..330af68385e
--- /dev/null
+++ b/app/controllers/import/gitlab_groups_controller.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+class Import::GitlabGroupsController < ApplicationController
+ include WorkhorseImportExportUpload
+
+ before_action :ensure_group_import_enabled
+ before_action :import_rate_limit, only: %i[create]
+
+ def create
+ unless file_is_valid?(group_params[:file])
+ return redirect_back_or_default(options: { alert: s_('GroupImport|Unable to process group import file') })
+ end
+
+ group_data = group_params.except(:file).merge(
+ visibility_level: closest_allowed_visibility_level,
+ import_export_upload: ImportExportUpload.new(import_file: group_params[:file])
+ )
+
+ group = ::Groups::CreateService.new(current_user, group_data).execute
+
+ if group.persisted?
+ if Groups::ImportExport::ImportService.new(group: group, user: current_user).async_execute
+ redirect_to(
+ group_path(group),
+ notice: s_("GroupImport|Group '%{group_name}' is being imported.") % { group_name: group.name }
+ )
+ else
+ redirect_to group_path(group), alert: _("Group import could not be scheduled")
+ end
+ else
+ redirect_back_or_default(
+ options: { alert: s_("GroupImport|Group could not be imported: %{errors}") % { errors: group.errors.full_messages.to_sentence } }
+ )
+ end
+ end
+
+ private
+
+ def group_params
+ params.permit(:path, :name, :parent_id, :file)
+ end
+
+ def closest_allowed_visibility_level
+ if group_params[:parent_id].present?
+ parent_group = Group.find(group_params[:parent_id])
+
+ Gitlab::VisibilityLevel.closest_allowed_level(parent_group.visibility_level)
+ else
+ Gitlab::VisibilityLevel::PRIVATE
+ end
+ end
+
+ def ensure_group_import_enabled
+ render_404 unless Feature.enabled?(:group_import_export, @group, default_enabled: true)
+ end
+
+ def import_rate_limit
+ if Gitlab::ApplicationRateLimiter.throttled?(:group_import, scope: current_user)
+ Gitlab::ApplicationRateLimiter.log_request(request, :group_import_request_limit, current_user)
+
+ flash[:alert] = _('This endpoint has been requested too many times. Try again later.')
+ redirect_to new_group_path
+ end
+ end
+end
diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb
index 6a3715a4675..39d053347f0 100644
--- a/app/controllers/import/gitlab_projects_controller.rb
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -1,14 +1,11 @@
# frozen_string_literal: true
class Import::GitlabProjectsController < Import::BaseController
- include WorkhorseRequest
+ include WorkhorseImportExportUpload
before_action :whitelist_query_limiting, only: [:create]
before_action :verify_gitlab_project_import_enabled
- skip_before_action :verify_authenticity_token, only: [:authorize]
- before_action :verify_workhorse_api!, only: [:authorize]
-
def new
@namespace = Namespace.find(project_params[:namespace_id])
return render_404 unless current_user.can?(:create_projects, @namespace)
@@ -17,7 +14,7 @@ class Import::GitlabProjectsController < Import::BaseController
end
def create
- unless file_is_valid?
+ unless file_is_valid?(project_params[:file])
return redirect_back_or_default(options: { alert: _("You need to upload a GitLab project export archive (ending in .gz).") })
end
@@ -33,28 +30,8 @@ class Import::GitlabProjectsController < Import::BaseController
end
end
- def authorize
- set_workhorse_internal_api_content_type
-
- authorized = ImportExportUploader.workhorse_authorize(
- has_length: false,
- maximum_size: Gitlab::CurrentSettings.max_attachment_size.megabytes.to_i)
-
- render json: authorized
- rescue SocketError
- render json: _("Error uploading file"), status: :internal_server_error
- end
-
private
- def file_is_valid?
- return false unless project_params[:file].is_a?(::UploadedFile)
-
- filename = project_params[:file].original_filename
-
- ImportExportUploader::EXTENSION_WHITELIST.include?(File.extname(filename).delete('.'))
- end
-
def verify_gitlab_project_import_enabled
render_404 unless gitlab_project_import_enabled?
end
diff --git a/app/controllers/projects/alert_management_controller.rb b/app/controllers/projects/alert_management_controller.rb
index 0c0a91e136f..054dc8e6a35 100644
--- a/app/controllers/projects/alert_management_controller.rb
+++ b/app/controllers/projects/alert_management_controller.rb
@@ -2,10 +2,6 @@
class Projects::AlertManagementController < Projects::ApplicationController
before_action :authorize_read_alert_management_alert!
- before_action do
- push_frontend_feature_flag(:alert_list_status_filtering_enabled)
- push_frontend_feature_flag(:create_issue_from_alert_enabled)
- end
def index
end
diff --git a/app/controllers/projects/alerting/notifications_controller.rb b/app/controllers/projects/alerting/notifications_controller.rb
index 358e7629958..fef8235628d 100644
--- a/app/controllers/projects/alerting/notifications_controller.rb
+++ b/app/controllers/projects/alerting/notifications_controller.rb
@@ -29,12 +29,22 @@ module Projects
end
def notify_service
- Projects::Alerting::NotifyService
- .new(project, current_user, notification_payload)
+ notify_service_class.new(project, current_user, notification_payload)
+ end
+
+ def notify_service_class
+ # We are tracking the consolidation of these services in
+ # https://gitlab.com/groups/gitlab-org/-/epics/3360
+ # to get rid of this workaround.
+ if Projects::Prometheus::Alerts::NotifyService.processable?(notification_payload)
+ Projects::Prometheus::Alerts::NotifyService
+ else
+ Projects::Alerting::NotifyService
+ end
end
def notification_payload
- params.permit![:notification]
+ @notification_payload ||= params.permit![:notification]
end
end
end
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index 66b51b17790..59a7dff680c 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -8,14 +8,21 @@ class Projects::BadgesController < Projects::ApplicationController
def pipeline
pipeline_status = Gitlab::Badge::Pipeline::Status
- .new(project, params[:ref])
+ .new(project, params[:ref], opts: {
+ key_text: params[:key_text],
+ key_width: params[:key_width]
+ })
render_badge pipeline_status
end
def coverage
coverage_report = Gitlab::Badge::Coverage::Report
- .new(project, params[:ref], params[:job])
+ .new(project, params[:ref], opts: {
+ job: params[:job],
+ key_text: params[:key_text],
+ key_width: params[:key_width]
+ })
render_badge coverage_report
end
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index b62ce940e9c..374b4921dbc 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -20,6 +20,7 @@ class Projects::BlameController < Projects::ApplicationController
environment_params[:find_latest] = true
@environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last
- @blame_groups = Gitlab::Blame.new(@blob, @commit).groups
+ @blame = Gitlab::Blame.new(@blob, @commit)
+ @blame = Gitlab::View::Presenter::Factory.new(@blame, project: @project, path: @path).fabricate!
end
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 584320a66de..abc1d58cbf1 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -71,6 +71,7 @@ class Projects::BlobController < Projects::ApplicationController
def update
@path = params[:file_path] if params[:file_path].present?
+
create_commit(Files::UpdateService, success_path: -> { after_edit_path },
failure_view: :edit,
failure_path: project_blob_path(@project, @id))
@@ -93,7 +94,6 @@ class Projects::BlobController < Projects::ApplicationController
def destroy
create_commit(Files::DeleteService, success_notice: _("The file has been successfully deleted."),
success_path: -> { after_delete_path },
- failure_view: :show,
failure_path: project_blob_path(@project, @id))
end
@@ -115,6 +115,8 @@ class Projects::BlobController < Projects::ApplicationController
private
+ attr_reader :branch_name
+
def blob
@blob ||= @repository.blob_at(@commit.id, @path)
@@ -254,3 +256,5 @@ class Projects::BlobController < Projects::ApplicationController
params.permit(:full, :since, :to, :bottom, :unfold, :offset, :indent)
end
end
+
+Projects::BlobController.prepend_if_ee('EE::Projects::BlobController')
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index cc595740696..7cfb4a508da 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -25,8 +25,9 @@ class Projects::BranchesController < Projects::ApplicationController
@refs_pipelines = @project.ci_pipelines.latest_successful_for_refs(@branches.map(&:name))
@merged_branch_names = repository.merged_branch_names(@branches.map(&:name))
+ @branch_pipeline_statuses = branch_pipeline_statuses
- # https://gitlab.com/gitlab-org/gitlab-foss/issues/48097
+ # https://gitlab.com/gitlab-org/gitlab/-/issues/22851
Gitlab::GitalyClient.allow_n_plus_1_calls do
render
end
@@ -194,4 +195,15 @@ class Projects::BranchesController < Projects::ApplicationController
confidential_issue_project
end
+
+ def branch_pipeline_statuses
+ latest_commits = @branches.map do |branch|
+ [branch.name, repository.commit(branch.dereferenced_target).sha]
+ end.to_h
+
+ latest_pipelines = project.ci_pipelines.latest_pipeline_per_commit(latest_commits.values)
+ latest_commits.transform_values do |commit_sha|
+ latest_pipelines[commit_sha]&.detailed_status(current_user)
+ end.compact
+ end
end
diff --git a/app/controllers/projects/ci/daily_build_group_report_results_controller.rb b/app/controllers/projects/ci/daily_build_group_report_results_controller.rb
index dfda5fca310..b36c5f1aea6 100644
--- a/app/controllers/projects/ci/daily_build_group_report_results_controller.rb
+++ b/app/controllers/projects/ci/daily_build_group_report_results_controller.rb
@@ -7,12 +7,13 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati
REPORT_WINDOW = 90.days
before_action :validate_feature_flag!
- before_action :authorize_download_code! # Share the same authorization rules as the graphs controller
+ before_action :authorize_read_build_report_results!
before_action :validate_param_type!
def index
respond_to do |format|
- format.csv { send_data(render_csv(results), type: 'text/csv; charset=utf-8') }
+ format.csv { send_data(render_csv(report_results), type: 'text/csv; charset=utf-8') }
+ format.json { render json: render_json(report_results) }
end
end
@@ -37,7 +38,11 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati
).render
end
- def results
+ def render_json(collection)
+ Ci::DailyBuildGroupReportResultSerializer.new.represent(collection, param_type: param_type)
+ end
+
+ def report_results
Ci::DailyBuildGroupReportResultsFinder.new(finder_params).execute
end
diff --git a/app/controllers/projects/discussions_controller.rb b/app/controllers/projects/discussions_controller.rb
index 028390c7e2a..06231607f73 100644
--- a/app/controllers/projects/discussions_controller.rb
+++ b/app/controllers/projects/discussions_controller.rb
@@ -10,7 +10,7 @@ class Projects::DiscussionsController < Projects::ApplicationController
before_action :authorize_resolve_discussion!, only: [:resolve, :unresolve]
def resolve
- Discussions::ResolveService.new(project, current_user, merge_request: merge_request).execute(discussion)
+ Discussions::ResolveService.new(project, current_user, one_or_more_discussions: discussion).execute
render_discussion
end
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 5f4d88c57e9..4d774123ef1 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -10,7 +10,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
push_frontend_feature_flag(:prometheus_computed_alerts)
end
- before_action :authorize_read_environment!
+ before_action :authorize_read_environment!, except: [:metrics, :additional_metrics, :metrics_dashboard, :metrics_redirect]
before_action :authorize_create_environment!, only: [:new, :create]
before_action :authorize_stop_environment!, only: [:stop]
before_action :authorize_update_environment!, only: [:edit, :update, :cancel_auto_stop]
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 34246f27241..a8b90f8685f 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -6,7 +6,7 @@ class Projects::GraphsController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :assign_ref_vars
- before_action :authorize_download_code!
+ before_action :authorize_read_repository_graphs!
def show
respond_to do |format|
@@ -54,7 +54,8 @@ class Projects::GraphsController < Projects::ApplicationController
end
def get_daily_coverage_options
- return unless Feature.enabled?(:ci_download_daily_code_coverage, default_enabled: true)
+ return unless Feature.enabled?(:ci_download_daily_code_coverage, @project, default_enabled: true)
+ return unless can?(current_user, :read_build_report_results, project)
date_today = Date.current
report_window = Projects::Ci::DailyBuildGroupReportResultsController::REPORT_WINDOW
@@ -70,6 +71,11 @@ class Projects::GraphsController < Projects::ApplicationController
namespace_id: @project.namespace,
project_id: @project,
format: :csv
+ ),
+ graph_api_path: namespace_project_ci_daily_build_group_report_results_path(
+ namespace_id: @project.namespace,
+ project_id: @project,
+ format: :json
)
}
end
diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb
index d06e24ef39c..a30c455a7e4 100644
--- a/app/controllers/projects/group_links_controller.rb
+++ b/app/controllers/projects/group_links_controller.rb
@@ -5,10 +5,6 @@ class Projects::GroupLinksController < Projects::ApplicationController
before_action :authorize_admin_project!
before_action :authorize_admin_project_member!, only: [:update]
- def index
- redirect_to namespace_project_settings_members_path
- end
-
def create
group = Group.find(params[:link_group_id]) if params[:link_group_id].present?
@@ -26,8 +22,7 @@ class Projects::GroupLinksController < Projects::ApplicationController
def update
@group_link = @project.project_group_links.find(params[:id])
-
- @group_link.update(group_link_params)
+ Projects::GroupLinks::UpdateService.new(@group_link).execute(group_link_params)
end
def destroy
diff --git a/app/controllers/projects/import/jira_controller.rb b/app/controllers/projects/import/jira_controller.rb
index 711e23dc3ce..976ac7df976 100644
--- a/app/controllers/projects/import/jira_controller.rb
+++ b/app/controllers/projects/import/jira_controller.rb
@@ -4,59 +4,29 @@ module Projects
module Import
class JiraController < Projects::ApplicationController
before_action :authenticate_user!
- before_action :check_issues_available!
before_action :authorize_read_project!
- before_action :jira_import_enabled?
- before_action :jira_integration_configured?
- before_action :authorize_admin_project!, only: [:import]
+ before_action :validate_jira_import_settings!
def show
- jira_service = @project.jira_service
-
- if jira_service.present? && !@project.latest_jira_import&.in_progress? && current_user&.can?(:admin_project, @project)
- jira_client = jira_service.client
- jira_projects = jira_client.Project.all
-
- if jira_projects.present?
- @jira_projects = jira_projects.map { |p| ["#{p.name} (#{p.key})", p.key] }
- else
- flash[:alert] = 'No projects have been returned from Jira. Please check your Jira configuration.'
- end
- end
-
- unless Feature.enabled?(:jira_issue_import_vue, @project, default_enabled: true)
- flash[:notice] = _("Import %{status}") % { status: @project.jira_import_status } unless @project.latest_jira_import&.initial?
- end
- end
-
- def import
- jira_project_key = jira_import_params[:jira_project_key]
-
- if jira_project_key.present?
- response = ::JiraImport::StartImportService.new(current_user, @project, jira_project_key).execute
- flash[:notice] = response.message if response.message.present?
- else
- flash[:alert] = 'No Jira project key has been provided.'
- end
-
- redirect_to project_import_jira_path(@project)
end
private
- def jira_import_enabled?
- return if @project.jira_issues_import_feature_flag_enabled?
+ def validate_jira_import_settings!
+ Gitlab::JiraImport.validate_project_settings!(@project, user: current_user, configuration_check: false)
+ true
+ rescue Projects::ImportService::Error => e
+ flash[:notice] = e.message
redirect_to project_issues_path(@project)
- end
- def jira_integration_configured?
- return if Feature.enabled?(:jira_issue_import_vue, @project, default_enabled: true)
- return if @project.jira_service
+ false
+ end
- flash[:notice] = _("Configure the Jira integration first on your project's %{strong_start} Settings > Integrations > Jira%{strong_end} page." %
- { strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe })
- redirect_to project_issues_path(@project)
+ def jira_service
+ strong_memoize(:jira_service) do
+ @project.jira_service
+ end
end
def jira_import_params
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 3e9d956f7b1..693329848de 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -46,7 +46,6 @@ class Projects::IssuesController < Projects::ApplicationController
before_action do
push_frontend_feature_flag(:vue_issuable_sidebar, project.group)
- push_frontend_feature_flag(:save_issuable_health_status, project.group, default_enabled: true)
end
before_action only: :show do
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index e0457925b34..e1f6cbe3dca 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -14,6 +14,8 @@ class Projects::JobsController < Projects::ApplicationController
before_action only: [:show] do
push_frontend_feature_flag(:job_log_json, project, default_enabled: true)
end
+ before_action :authorize_create_proxy_build!, only: :proxy_websocket_authorize
+ before_action :verify_proxy_request!, only: :proxy_websocket_authorize
layout 'project'
@@ -151,6 +153,10 @@ class Projects::JobsController < Projects::ApplicationController
render json: Gitlab::Workhorse.channel_websocket(@build.terminal_specification)
end
+ def proxy_websocket_authorize
+ render json: proxy_websocket_service(build_service_specification)
+ end
+
private
def authorize_update_build!
@@ -165,10 +171,19 @@ class Projects::JobsController < Projects::ApplicationController
return access_denied! unless can?(current_user, :create_build_terminal, build)
end
+ def authorize_create_proxy_build!
+ return access_denied! unless can?(current_user, :create_build_service_proxy, build)
+ end
+
def verify_api_request!
Gitlab::Workhorse.verify_api_request!(request.headers)
end
+ def verify_proxy_request!
+ verify_api_request!
+ set_workhorse_internal_api_content_type
+ end
+
def raw_send_params
{ type: 'text/plain; charset=utf-8', disposition: 'inline' }
end
@@ -202,6 +217,27 @@ class Projects::JobsController < Projects::ApplicationController
'attachment'
end
-end
-Projects::JobsController.prepend_if_ee('EE::Projects::JobsController')
+ def build_service_specification
+ build.service_specification(service: params['service'],
+ port: params['port'],
+ path: params['path'],
+ subprotocols: proxy_subprotocol)
+ end
+
+ def proxy_subprotocol
+ # This will allow to reuse the same subprotocol set
+ # in the original websocket connection
+ request.headers['HTTP_SEC_WEBSOCKET_PROTOCOL'].presence || ::Ci::BuildRunnerSession::TERMINAL_SUBPROTOCOL
+ end
+
+ # This method provides the information to Workhorse
+ # about the service we want to proxy to.
+ # For security reasons, in case this operation is started by JS,
+ # it's important to use only sourced GitLab JS code
+ def proxy_websocket_service(service)
+ service[:url] = ::Gitlab::UrlHelpers.as_wss(service[:url])
+
+ ::Gitlab::Workhorse.channel_websocket(service)
+ end
+end
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index 2331674f42c..1bf143c9a91 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -162,8 +162,13 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
def renderable_notes
define_diff_comment_vars unless @notes
- @notes
+ draft_notes =
+ if current_user
+ merge_request.draft_notes.authored_by(current_user)
+ else
+ []
+ end
+
+ @notes.concat(draft_notes)
end
end
-
-Projects::MergeRequests::DiffsController.prepend_if_ee('EE::Projects::MergeRequests::DiffsController')
diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb
new file mode 100644
index 00000000000..f4846b1aa81
--- /dev/null
+++ b/app/controllers/projects/merge_requests/drafts_controller.rb
@@ -0,0 +1,129 @@
+# frozen_string_literal: true
+
+class Projects::MergeRequests::DraftsController < Projects::MergeRequests::ApplicationController
+ include Gitlab::Utils::StrongMemoize
+
+ respond_to :json
+
+ before_action :authorize_create_note!, only: [:create, :publish]
+ before_action :authorize_admin_draft!, only: [:update, :destroy]
+ before_action :authorize_admin_draft!, if: -> { action_name == 'publish' && params[:id].present? }
+
+ def index
+ drafts = prepare_notes_for_rendering(draft_notes)
+ render json: DraftNoteSerializer.new(current_user: current_user).represent(drafts)
+ end
+
+ def create
+ create_params = draft_note_params.merge(in_reply_to_discussion_id: params[:in_reply_to_discussion_id])
+ create_service = DraftNotes::CreateService.new(merge_request, current_user, create_params)
+
+ draft_note = create_service.execute
+
+ prepare_notes_for_rendering(draft_note)
+
+ render json: DraftNoteSerializer.new(current_user: current_user).represent(draft_note)
+ end
+
+ def update
+ draft_note.update!(draft_note_params)
+
+ prepare_notes_for_rendering(draft_note)
+
+ render json: DraftNoteSerializer.new(current_user: current_user).represent(draft_note)
+ end
+
+ def destroy
+ DraftNotes::DestroyService.new(merge_request, current_user).execute(draft_note)
+
+ head :ok
+ end
+
+ def publish
+ result = DraftNotes::PublishService.new(merge_request, current_user).execute(draft_note(allow_nil: true))
+
+ if result[:status] == :success
+ head :ok
+ else
+ render json: { message: result[:message] }, status: result[:status]
+ end
+ end
+
+ def discard
+ DraftNotes::DestroyService.new(merge_request, current_user).execute
+
+ head :ok
+ end
+
+ private
+
+ def draft_note(allow_nil: false)
+ strong_memoize(:draft_note) do
+ draft_notes.find(params[:id])
+ end
+ rescue ActiveRecord::RecordNotFound => ex
+ # draft_note is allowed to be nil in #publish
+ raise ex unless allow_nil
+ end
+
+ def draft_notes
+ return unless current_user
+
+ strong_memoize(:draft_notes) do
+ merge_request.draft_notes.authored_by(current_user)
+ end
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def merge_request
+ @merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).find_by!(iid: params[:merge_request_id])
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def draft_note_params
+ params.require(:draft_note).permit(
+ :commit_id,
+ :note,
+ :position,
+ :resolve_discussion
+ ).tap do |h|
+ # Old FE version will still be sending `draft_note[commit_id]` as 'undefined'.
+ # That can result to having a note linked to a commit with 'undefined' ID
+ # which is non-existent.
+ h[:commit_id] = nil if h[:commit_id] == 'undefined'
+ end
+ end
+
+ def prepare_notes_for_rendering(notes)
+ return [] unless notes
+
+ notes = Array.wrap(notes)
+
+ # Preload author and access-level information
+ DraftNote.preload_author(notes)
+ user_ids = notes.map(&:author_id)
+ project.team.max_member_access_for_user_ids(user_ids)
+
+ notes.map(&method(:render_draft_note))
+ end
+
+ def render_draft_note(note)
+ params = { target_id: merge_request.id, target_type: 'MergeRequest', text: note.note }
+ result = PreviewMarkdownService.new(@project, current_user, params).execute
+ markdown_params = { markdown_engine: result[:markdown_engine], issuable_state_filter_enabled: true }
+
+ note.rendered_note = view_context.markdown(result[:text], markdown_params)
+ note.users_referenced = result[:users]
+ note.commands_changes = view_context.markdown(result[:commands])
+
+ note
+ end
+
+ def authorize_admin_draft!
+ access_denied! unless can?(current_user, :admin_note, draft_note)
+ end
+
+ def authorize_create_note!
+ access_denied! unless can?(current_user, :create_note, merge_request)
+ end
+end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 5613b5b9589..55556ea7d31 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -33,6 +33,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:widget_visibility_polling, @project, default_enabled: true)
push_frontend_feature_flag(:merge_ref_head_comments, @project)
push_frontend_feature_flag(:mr_commit_neighbor_nav, @project, default_enabled: true)
+ push_frontend_feature_flag(:multiline_comments, @project)
+ push_frontend_feature_flag(:file_identifier_hash)
+ push_frontend_feature_flag(:batch_suggestions, @project)
end
before_action do
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 56f1f1a1019..16d63cc184f 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -7,7 +7,7 @@ class Projects::MilestonesController < Projects::ApplicationController
before_action :check_issuables_available!
before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels, :promote]
before_action do
- push_frontend_feature_flag(:burnup_charts)
+ push_frontend_feature_flag(:burnup_charts, @project)
end
# Allow read any milestone
@@ -34,7 +34,7 @@ class Projects::MilestonesController < Projects::ApplicationController
@milestones = @milestones.page(params[:page])
end
format.json do
- render json: @milestones.to_json(methods: :name)
+ render json: @milestones.to_json(only: [:id, :title], methods: :name)
end
end
end
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 678d0862f48..0b6c0db211e 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -12,8 +12,9 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action do
push_frontend_feature_flag(:junit_pipeline_view, project)
- push_frontend_feature_flag(:filter_pipelines_search, default_enabled: true)
- push_frontend_feature_flag(:dag_pipeline_tab)
+ push_frontend_feature_flag(:filter_pipelines_search, project, default_enabled: true)
+ push_frontend_feature_flag(:dag_pipeline_tab, project, default_enabled: false)
+ push_frontend_feature_flag(:pipelines_security_report_summary, project)
end
before_action :ensure_pipeline, only: [:show]
@@ -95,7 +96,14 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def dag
- render_show
+ respond_to do |format|
+ format.html { render_show }
+ format.json do
+ render json: Ci::DagPipelineSerializer
+ .new(project: @project, current_user: @current_user)
+ .represent(@pipeline)
+ end
+ end
end
def failures
@@ -269,7 +277,7 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def index_params
- params.permit(:scope, :username, :ref)
+ params.permit(:scope, :username, :ref, :status)
end
end
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index fcbeb5c840c..a2581e72257 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -53,7 +53,7 @@ class Projects::RefsController < Projects::ApplicationController
format.json do
logs, next_offset = tree_summary.fetch_logs
- response.headers["More-Logs-Offset"] = next_offset if next_offset
+ response.headers["More-Logs-Offset"] = next_offset.to_s if next_offset
render json: logs
end
diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb
index 228b139d794..d3285b64dab 100644
--- a/app/controllers/projects/releases_controller.rb
+++ b/app/controllers/projects/releases_controller.rb
@@ -10,6 +10,7 @@ class Projects::ReleasesController < Projects::ApplicationController
push_frontend_feature_flag(:release_evidence_collection, project, default_enabled: true)
push_frontend_feature_flag(:release_show_page, project, default_enabled: true)
push_frontend_feature_flag(:release_asset_link_editing, project, default_enabled: true)
+ push_frontend_feature_flag(:release_asset_link_type, project, default_enabled: true)
end
before_action :authorize_update_release!, only: %i[edit update]
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 92c6ce324f7..710ad546e64 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -2,6 +2,7 @@
class Projects::ServicesController < Projects::ApplicationController
include ServiceParams
+ include InternalRedirect
# Authorize
before_action :authorize_admin_project!
@@ -10,6 +11,9 @@ class Projects::ServicesController < Projects::ApplicationController
before_action :web_hook_logs, only: [:edit, :update]
before_action :set_deprecation_notice_for_prometheus_service, only: [:edit, :update]
before_action :redirect_deprecated_prometheus_service, only: [:update]
+ before_action only: :edit do
+ push_frontend_feature_flag(:integration_form_refactor)
+ end
respond_to :html
@@ -26,8 +30,8 @@ class Projects::ServicesController < Projects::ApplicationController
respond_to do |format|
format.html do
if saved
- redirect_to project_settings_integrations_path(@project),
- notice: success_message
+ target_url = safe_redirect_path(params[:redirect_to]).presence || project_settings_integrations_path(@project)
+ redirect_to target_url, notice: success_message
else
render 'edit'
end
@@ -56,11 +60,10 @@ class Projects::ServicesController < Projects::ApplicationController
return { error: true, message: _('Validations failed.'), service_response: @service.errors.full_messages.join(','), test_failed: false }
end
- data = @service.test_data(project, current_user)
- outcome = @service.test(data)
+ result = Integrations::Test::ProjectService.new(@service, current_user, params[:event]).execute
- unless outcome[:success]
- return { error: true, message: _('Test failed.'), service_response: outcome[:result].to_s, test_failed: true }
+ unless result[:success]
+ return { error: true, message: _('Test failed.'), service_response: result[:message].to_s, test_failed: true }
end
{}
diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb
index a9d1dc0759d..c2292511e0f 100644
--- a/app/controllers/projects/settings/operations_controller.rb
+++ b/app/controllers/projects/settings/operations_controller.rb
@@ -104,7 +104,7 @@ module Projects
project_params = {
incident_management_setting_attributes: ::Gitlab::Tracking::IncidentManagement.tracking_keys.keys,
- metrics_setting_attributes: [:external_dashboard_url],
+ metrics_setting_attributes: [:external_dashboard_url, :dashboard_timezone],
error_tracking_setting_attributes: [
:enabled,
diff --git a/app/controllers/projects/snippets/application_controller.rb b/app/controllers/projects/snippets/application_controller.rb
new file mode 100644
index 00000000000..3f488b07e96
--- /dev/null
+++ b/app/controllers/projects/snippets/application_controller.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class Projects::Snippets::ApplicationController < Projects::ApplicationController
+ include FindSnippet
+ include SnippetAuthorizations
+
+ private
+
+ # This overrides the default snippet create authorization
+ # because ProjectSnippets are checked against the project rather
+ # than the user
+ def authorize_create_snippet!
+ return render_404 unless can?(current_user, :create_snippet, project)
+ end
+
+ def snippet_klass
+ ProjectSnippet
+ end
+end
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 9233f063f55..5ee6abef804 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -1,34 +1,19 @@
# frozen_string_literal: true
-class Projects::SnippetsController < Projects::ApplicationController
- include RendersNotes
+class Projects::SnippetsController < Projects::Snippets::ApplicationController
+ include SnippetsActions
include ToggleAwardEmoji
include SpammableActions
- include SnippetsActions
- include RendersBlob
- include PaginatedCollection
- include Gitlab::NoteableMetadata
-
- skip_before_action :verify_authenticity_token,
- if: -> { action_name == 'show' && js_request? }
before_action :check_snippets_available!
+
before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
- # Allow create snippet
before_action :authorize_create_snippet!, only: [:new, :create]
-
- # Allow read any snippet
before_action :authorize_read_snippet!, except: [:new, :create, :index]
-
- # Allow modify snippet
before_action :authorize_update_snippet!, only: [:edit, :update]
-
- # Allow destroy snippet
before_action :authorize_admin_snippet!, only: [:destroy]
- respond_to :html
-
def index
@snippet_counts = Snippets::CountService
.new(current_user, project: @project)
@@ -56,61 +41,8 @@ class Projects::SnippetsController < Projects::ApplicationController
handle_repository_error(:new)
end
- def update
- update_params = snippet_params.merge(spammable_params)
-
- service_response = Snippets::UpdateService.new(project, current_user, update_params).execute(@snippet)
- @snippet = service_response.payload[:snippet]
-
- handle_repository_error(:edit)
- end
-
- def show
- conditionally_expand_blob(blob)
-
- respond_to do |format|
- format.html do
- @note = @project.notes.new(noteable: @snippet)
- @noteable = @snippet
-
- @discussions = @snippet.discussions
- @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
- render 'show'
- end
-
- format.json do
- render_blob_json(blob)
- end
-
- format.js do
- if @snippet.embeddable?
- render 'shared/snippets/show'
- else
- head :not_found
- end
- end
- end
- end
-
- def destroy
- service_response = Snippets::DestroyService.new(current_user, @snippet).execute
-
- if service_response.success?
- redirect_to project_snippets_path(project), status: :found
- elsif service_response.http_status == 403
- access_denied!
- else
- redirect_to project_snippet_path(project, @snippet),
- status: :found,
- alert: service_response.message
- end
- end
-
protected
- def snippet
- @snippet ||= @project.snippets.inc_relations_for_view.find(params[:id])
- end
alias_method :awardable, :snippet
alias_method :spammable, :snippet
@@ -118,18 +50,6 @@ class Projects::SnippetsController < Projects::ApplicationController
project_snippet_path(@project, @snippet)
end
- def authorize_read_snippet!
- return render_404 unless can?(current_user, :read_snippet, @snippet)
- end
-
- def authorize_update_snippet!
- return render_404 unless can?(current_user, :update_snippet, @snippet)
- end
-
- def authorize_admin_snippet!
- return render_404 unless can?(current_user, :admin_snippet, @snippet)
- end
-
def snippet_params
params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description)
end
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index c89bfd110c4..df20daa8f7e 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -41,16 +41,20 @@ class Projects::TagsController < Projects::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord
def create
+ # TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
+ evidence_pipeline = find_evidence_pipeline
+
result = ::Tags::CreateService.new(@project, current_user)
.execute(params[:tag_name], params[:ref], params[:message])
if result[:status] == :success
- # Release creation with Tags was deprecated in GitLab 11.7
+ # TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
if params[:release_description].present?
release_params = {
tag: params[:tag_name],
name: params[:tag_name],
- description: params[:release_description]
+ description: params[:release_description],
+ evidence_pipeline: evidence_pipeline
}
Releases::CreateService
@@ -93,4 +97,14 @@ class Projects::TagsController < Projects::ApplicationController
end
end
end
+
+ private
+
+ # TODO: remove this with the release creation moved to it's own form https://gitlab.com/gitlab-org/gitlab/-/issues/214245
+ def find_evidence_pipeline
+ evidence_pipeline_sha = @project.repository.commit(params[:ref])&.sha
+ return unless evidence_pipeline_sha
+
+ @project.ci_pipelines.for_sha(evidence_pipeline_sha).last
+ end
end
diff --git a/app/controllers/projects/web_ide_terminals_controller.rb b/app/controllers/projects/web_ide_terminals_controller.rb
new file mode 100644
index 00000000000..08ea5c4bca8
--- /dev/null
+++ b/app/controllers/projects/web_ide_terminals_controller.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+
+class Projects::WebIdeTerminalsController < Projects::ApplicationController
+ before_action :authenticate_user!
+
+ before_action :build, except: [:check_config, :create]
+ before_action :authorize_create_web_ide_terminal!
+ before_action :authorize_read_web_ide_terminal!, except: [:check_config, :create]
+ before_action :authorize_update_web_ide_terminal!, only: [:cancel, :retry]
+
+ def check_config
+ return respond_422 unless branch_sha
+
+ result = ::Ci::WebIdeConfigService.new(project, current_user, sha: branch_sha).execute
+
+ if result[:status] == :success
+ head :ok
+ else
+ respond_422
+ end
+ end
+
+ def show
+ render_terminal(build)
+ end
+
+ def create
+ result = ::Ci::CreateWebIdeTerminalService.new(project,
+ current_user,
+ ref: params[:branch])
+ .execute
+
+ if result[:status] == :error
+ render status: :bad_request, json: result[:message]
+ else
+ pipeline = result[:pipeline]
+ current_build = pipeline.builds.last
+
+ if current_build
+ Gitlab::UsageDataCounters::WebIdeCounter.increment_terminals_count
+
+ render_terminal(current_build)
+ else
+ render status: :bad_request, json: pipeline.errors.full_messages
+ end
+ end
+ end
+
+ def cancel
+ return respond_422 unless build.cancelable?
+
+ build.cancel
+
+ head :ok
+ end
+
+ def retry
+ return respond_422 unless build.retryable?
+
+ new_build = Ci::Build.retry(build, current_user)
+
+ render_terminal(new_build)
+ end
+
+ private
+
+ def authorize_create_web_ide_terminal!
+ return access_denied! unless can?(current_user, :create_web_ide_terminal, project)
+ end
+
+ def authorize_read_web_ide_terminal!
+ authorize_build_ability!(:read_web_ide_terminal)
+ end
+
+ def authorize_update_web_ide_terminal!
+ authorize_build_ability!(:update_web_ide_terminal)
+ end
+
+ def authorize_build_ability!(ability)
+ return access_denied! unless can?(current_user, ability, build)
+ end
+
+ def build
+ @build ||= project.builds.find(params[:id])
+ end
+
+ def branch_sha
+ return unless params[:branch].present?
+
+ project.commit(params[:branch])&.id
+ end
+
+ def render_terminal(current_build)
+ render json: WebIdeTerminalSerializer
+ .new(project: project, current_user: current_user)
+ .represent(current_build)
+ end
+end
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 508b1f5bd0a..85e643aa212 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -1,206 +1,11 @@
# frozen_string_literal: true
class Projects::WikisController < Projects::ApplicationController
+ include WikiActions
include PreviewMarkdown
- include SendsBlob
- include Gitlab::Utils::StrongMemoize
- before_action :authorize_read_wiki!
- before_action :authorize_create_wiki!, only: [:edit, :create]
- before_action :authorize_admin_wiki!, only: :destroy
- before_action :load_project_wiki
- before_action :load_page, only: [:show, :edit, :update, :history, :destroy]
- before_action only: [:show, :edit, :update] do
- @valid_encoding = valid_encoding?
- end
- before_action only: [:edit, :update], unless: :valid_encoding? do
- redirect_to(project_wiki_path(@project, @page))
- end
-
- def new
- redirect_to project_wiki_path(@project, SecureRandom.uuid, random_title: true)
- end
-
- def pages
- @wiki_pages = Kaminari.paginate_array(
- @project_wiki.list_pages(sort: params[:sort], direction: params[:direction])
- ).page(params[:page])
-
- @wiki_entries = WikiPage.group_by_directory(@wiki_pages)
- end
-
- # `#show` handles a number of scenarios:
- #
- # - If `id` matches a WikiPage, then show the wiki page.
- # - If `id` is a file in the wiki repository, then send the file.
- # - If we know the user wants to create a new page with the given `id`,
- # then display a create form.
- # - Otherwise show the empty wiki page and invite the user to create a page.
- def show
- if @page
- set_encoding_error unless valid_encoding?
-
- # Assign vars expected by MarkupHelper
- @ref = params[:version_id]
- @path = @page.path
-
- render 'show'
- elsif file_blob
- send_blob(@project_wiki.repository, file_blob, allow_caching: @project.public?)
- elsif show_create_form?
- # Assign a title to the WikiPage unless `id` is a randomly generated slug from #new
- title = params[:id] unless params[:random_title].present?
-
- @page = build_page(title: title)
-
- render 'edit'
- else
- render 'empty'
- end
- end
-
- def edit
- end
-
- def update
- return render('empty') unless can?(current_user, :create_wiki, @project)
-
- @page = WikiPages::UpdateService.new(container: @project, current_user: current_user, params: wiki_params).execute(@page)
-
- if @page.valid?
- redirect_to(
- project_wiki_path(@project, @page),
- notice: _('Wiki was successfully updated.')
- )
- else
- render 'edit'
- end
- rescue WikiPage::PageChangedError, WikiPage::PageRenameError, Gitlab::Git::Wiki::OperationError => e
- @error = e
- render 'edit'
- end
-
- def create
- @page = WikiPages::CreateService.new(container: @project, current_user: current_user, params: wiki_params).execute
-
- if @page.persisted?
- redirect_to(
- project_wiki_path(@project, @page),
- notice: _('Wiki was successfully updated.')
- )
- else
- render action: "edit"
- end
- rescue Gitlab::Git::Wiki::OperationError => e
- @page = build_page(wiki_params)
- @error = e
-
- render 'edit'
- end
-
- def history
- if @page
- @page_versions = Kaminari.paginate_array(@page.versions(page: params[:page].to_i),
- total_count: @page.count_versions)
- .page(params[:page])
- else
- redirect_to(
- project_wiki_path(@project, :home),
- notice: _("Page not found")
- )
- end
- end
-
- def destroy
- WikiPages::DestroyService.new(container: @project, current_user: current_user).execute(@page)
-
- redirect_to project_wiki_path(@project, :home),
- status: :found,
- notice: _("Page was successfully deleted")
- rescue Gitlab::Git::Wiki::OperationError => e
- @error = e
- render 'edit'
- end
+ alias_method :container, :project
def git_access
end
-
- private
-
- def show_create_form?
- can?(current_user, :create_wiki, @project) &&
- @page.nil? &&
- # Always show the create form when the wiki has had at least one page created.
- # Otherwise, we only show the form when the user has navigated from
- # the 'empty wiki' page
- (@project_wiki.exists? || params[:view] == 'create')
- end
-
- def load_project_wiki
- @project_wiki = load_wiki
-
- # Call #wiki to make sure the Wiki Repo is initialized
- @project_wiki.wiki
-
- @sidebar_page = @project_wiki.find_sidebar(params[:version_id])
-
- unless @sidebar_page # Fallback to default sidebar
- @sidebar_wiki_entries, @sidebar_limited = @project_wiki.sidebar_entries
- end
- rescue ProjectWiki::CouldNotCreateWikiError
- flash[:notice] = _("Could not create Wiki Repository at this time. Please try again later.")
- redirect_to project_path(@project)
- false
- end
-
- def load_wiki
- ProjectWiki.new(@project, current_user)
- end
-
- def wiki_params
- params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha)
- end
-
- def build_page(args = {})
- WikiPage.new(@project_wiki).tap do |page|
- page.update_attributes(args) # rubocop:disable Rails/ActiveRecordAliases
- end
- end
-
- def load_page
- @page ||= find_page
- end
-
- def find_page
- @project_wiki.find_page(*page_params)
- end
-
- def page_params
- keys = [:id]
- keys << :version_id if params[:action] == 'show'
-
- params.values_at(*keys)
- end
-
- def valid_encoding?
- page_encoding == Encoding::UTF_8
- end
-
- def page_encoding
- strong_memoize(:page_encoding) { @page&.content&.encoding }
- end
-
- def set_encoding_error
- flash.now[:notice] = _("The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.")
- end
-
- def file_blob
- strong_memoize(:file_blob) do
- commit = @project_wiki.repository.commit(@project_wiki.default_branch)
-
- next unless commit
-
- @project_wiki.repository.blob_at(commit.id, params[:id])
- end
- end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 2f86b945b06..f0ddd62e996 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -34,6 +34,12 @@ class ProjectsController < Projects::ApplicationController
# Project Export Rate Limit
before_action :export_rate_limit, only: [:export, :download_export, :generate_new_export]
+ # Experiments
+ before_action only: [:new, :create] do
+ frontend_experimentation_tracking_data(:new_create_project_ui, 'click_tab')
+ push_frontend_feature_flag(:new_create_project_ui) if experiment_enabled?(:new_create_project_ui)
+ end
+
layout :determine_layout
def index
@@ -310,12 +316,11 @@ class ProjectsController < Projects::ApplicationController
render 'projects/empty' if @project.empty_repo?
else
if can?(current_user, :read_wiki, @project)
- @project_wiki = @project.wiki
- @wiki_home = @project_wiki.find_page('home', params[:version_id])
+ @wiki = @project.wiki
+ @wiki_home = @wiki.find_page('home', params[:version_id])
elsif @project.feature_available?(:issues, current_user)
@issues = issuables_collection.page(params[:page])
- @collection_type = 'Issue'
- @issuable_meta_data = issuable_meta_data(@issues, @collection_type, current_user)
+ @issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @issues).data
end
render :show
@@ -357,6 +362,7 @@ class ProjectsController < Projects::ApplicationController
def project_params_attributes
[
+ :allow_merge_on_skipped_pipeline,
:avatar,
:build_allow_git_fetch,
:build_coverage_regex,
@@ -483,11 +489,12 @@ class ProjectsController < Projects::ApplicationController
def export_rate_limit
prefixed_action = "project_#{params[:action]}".to_sym
- if rate_limiter.throttled?(prefixed_action, scope: [current_user, prefixed_action, @project])
+ project_scope = params[:action] == :download_export ? @project : nil
+
+ if rate_limiter.throttled?(prefixed_action, scope: [current_user, project_scope].compact)
rate_limiter.log_request(request, "#{prefixed_action}_request_limit".to_sym, current_user)
- flash[:alert] = _('This endpoint has been requested too many times. Try again later.')
- redirect_to edit_project_path(@project)
+ render plain: _('This endpoint has been requested too many times. Try again later.'), status: :too_many_requests
end
end
diff --git a/app/controllers/registrations/experience_levels_controller.rb b/app/controllers/registrations/experience_levels_controller.rb
new file mode 100644
index 00000000000..515d6b3f9aa
--- /dev/null
+++ b/app/controllers/registrations/experience_levels_controller.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Registrations
+ class ExperienceLevelsController < ApplicationController
+ # This will need to be changed to simply 'devise' as part of
+ # https://gitlab.com/gitlab-org/growth/engineering/issues/64
+ layout 'devise_experimental_separate_sign_up_flow'
+
+ before_action :check_experiment_enabled
+ before_action :ensure_namespace_path_param
+
+ def update
+ current_user.experience_level = params[:experience_level]
+
+ if current_user.save
+ hide_advanced_issues
+ flash[:message] = I18n.t('devise.registrations.signed_up')
+ redirect_to group_path(params[:namespace_path])
+ else
+ render :show
+ end
+ end
+
+ private
+
+ def check_experiment_enabled
+ access_denied! unless experiment_enabled?(:onboarding_issues)
+ end
+
+ def ensure_namespace_path_param
+ redirect_to root_path unless params[:namespace_path].present?
+ end
+
+ def hide_advanced_issues
+ return unless current_user.user_preference.novice?
+
+ settings = cookies[:onboarding_issues_settings]
+ return unless settings
+
+ modified_settings = Gitlab::Json.parse(settings).merge(hideAdvanced: true)
+ cookies[:onboarding_issues_settings] = modified_settings.to_json
+ end
+ end
+end
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index ffbccbb01f2..6ab2924a8b5 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -63,6 +63,10 @@ class RegistrationsController < Devise::RegistrationsController
if result[:status] == :success
track_experiment_event(:signup_flow, 'end') # We want this event to be tracked when the user is _in_ the experimental group
+
+ track_experiment_event(:onboarding_issues, 'signed_up') if ::Gitlab.com? && !helpers.in_subscription_flow? && !helpers.in_invitation_flow?
+ return redirect_to new_users_sign_up_group_path if experiment_enabled?(:onboarding_issues) && !helpers.in_subscription_flow? && !helpers.in_invitation_flow?
+
set_flash_message! :notice, :signed_up
redirect_to path_for_signed_in_user(current_user)
else
diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb
index e3dbe6fcbdf..6a27d63625e 100644
--- a/app/controllers/repositories/git_http_controller.rb
+++ b/app/controllers/repositories/git_http_controller.rb
@@ -9,7 +9,7 @@ module Repositories
rescue_from Gitlab::GitAccess::ForbiddenError, with: :render_403_with_exception
rescue_from Gitlab::GitAccess::NotFoundError, with: :render_404_with_exception
- rescue_from Gitlab::GitAccess::ProjectCreationError, with: :render_422_with_exception
+ rescue_from Gitlab::GitAccessProject::CreationError, with: :render_422_with_exception
rescue_from Gitlab::GitAccess::TimeoutError, with: :render_503_with_exception
# GET /foo/bar.git/info/refs?service=git-upload-pack (git pull)
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 04d2b3068da..217f08dd648 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -5,6 +5,10 @@ class SearchController < ApplicationController
include SearchHelper
include RendersCommits
+ SCOPE_PRELOAD_METHOD = {
+ projects: :with_web_entity_associations
+ }.freeze
+
around_action :allow_gitaly_ref_name_caching
skip_before_action :authenticate_user!
@@ -28,12 +32,12 @@ class SearchController < ApplicationController
@scope = search_service.scope
@show_snippets = search_service.show_snippets?
@search_results = search_service.search_results
- @search_objects = search_service.search_objects
+ @search_objects = search_service.search_objects(preload_method)
render_commits if @scope == 'commits'
eager_load_user_status if @scope == 'users'
- increment_navbar_searches_counter
+ increment_search_counters
check_single_commit_result
end
@@ -47,22 +51,11 @@ class SearchController < ApplicationController
render json: { count: count }
end
- # rubocop: disable CodeReuse/ActiveRecord
- def autocomplete
- term = params[:term]
-
- if params[:project_id].present?
- @project = Project.find_by(id: params[:project_id])
- @project = nil unless can?(current_user, :read_project, @project)
- end
-
- @ref = params[:project_ref] if params[:project_ref].present?
+ private
- render json: search_autocomplete_opts(term).to_json
+ def preload_method
+ SCOPE_PRELOAD_METHOD[@scope.to_sym]
end
- # rubocop: enable CodeReuse/ActiveRecord
-
- private
def search_term_valid?
unless search_service.valid_query_length?
@@ -98,9 +91,11 @@ class SearchController < ApplicationController
end
end
- def increment_navbar_searches_counter
+ def increment_search_counters
+ Gitlab::UsageDataCounters::SearchCounter.count(:all_searches)
+
return if params[:nav_source] != 'navbar'
- Gitlab::UsageDataCounters::SearchCounter.increment_navbar_searches_count
+ Gitlab::UsageDataCounters::SearchCounter.count(:navbar_searches)
end
end
diff --git a/app/controllers/snippets/application_controller.rb b/app/controllers/snippets/application_controller.rb
new file mode 100644
index 00000000000..a533e46a75d
--- /dev/null
+++ b/app/controllers/snippets/application_controller.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class Snippets::ApplicationController < ApplicationController
+ include FindSnippet
+ include SnippetAuthorizations
+
+ private
+
+ def authorize_read_snippet!
+ return if can?(current_user, :read_snippet, snippet)
+
+ if current_user
+ render_404
+ else
+ authenticate_user!
+ end
+ end
+
+ def snippet_klass
+ PersonalSnippet
+ end
+end
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index 425e0458b41..87d87390e57 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -1,19 +1,12 @@
# frozen_string_literal: true
-class SnippetsController < ApplicationController
- include RendersNotes
- include ToggleAwardEmoji
- include SpammableActions
+class SnippetsController < Snippets::ApplicationController
include SnippetsActions
- include RendersBlob
include PreviewMarkdown
- include PaginatedCollection
- include Gitlab::NoteableMetadata
-
- skip_before_action :verify_authenticity_token,
- if: -> { action_name == 'show' && js_request? }
+ include ToggleAwardEmoji
+ include SpammableActions
- before_action :snippet, only: [:show, :edit, :destroy, :update, :raw]
+ before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
before_action :authorize_create_snippet!, only: [:new, :create]
before_action :authorize_read_snippet!, only: [:show, :raw]
@@ -23,7 +16,6 @@ class SnippetsController < ApplicationController
skip_before_action :authenticate_user!, only: [:index, :show, :raw]
layout 'snippets'
- respond_to :html
def index
if params[:username].present?
@@ -60,62 +52,8 @@ class SnippetsController < ApplicationController
end
end
- def update
- service_response = Snippets::UpdateService.new(nil, current_user, snippet_params).execute(@snippet)
- @snippet = service_response.payload[:snippet]
-
- handle_repository_error(:edit)
- end
-
- def show
- conditionally_expand_blob(blob)
-
- respond_to do |format|
- format.html do
- @note = Note.new(noteable: @snippet)
- @noteable = @snippet
-
- @discussions = @snippet.discussions
- @notes = prepare_notes_for_rendering(@discussions.flat_map(&:notes), @noteable)
- render 'show'
- end
-
- format.json do
- render_blob_json(blob)
- end
-
- format.js do
- if @snippet.embeddable?
- render 'shared/snippets/show'
- else
- head :not_found
- end
- end
- end
- end
-
- def destroy
- service_response = Snippets::DestroyService.new(current_user, @snippet).execute
-
- if service_response.success?
- redirect_to dashboard_snippets_path, status: :found
- elsif service_response.http_status == 403
- access_denied!
- else
- redirect_to snippet_path(@snippet),
- status: :found,
- alert: service_response.message
- end
- end
-
protected
- # rubocop: disable CodeReuse/ActiveRecord
- def snippet
- @snippet ||= PersonalSnippet.inc_relations_for_view.find_by(id: params[:id])
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
alias_method :awardable, :snippet
alias_method :spammable, :snippet
@@ -123,28 +61,6 @@ class SnippetsController < ApplicationController
snippet_path(@snippet)
end
- def authorize_read_snippet!
- return if can?(current_user, :read_snippet, @snippet)
-
- if current_user
- render_404
- else
- authenticate_user!
- end
- end
-
- def authorize_update_snippet!
- return render_404 unless can?(current_user, :update_snippet, @snippet)
- end
-
- def authorize_admin_snippet!
- return render_404 unless can?(current_user, :admin_snippet, @snippet)
- end
-
- def authorize_create_snippet!
- return render_404 unless can?(current_user, :create_snippet)
- end
-
def snippet_params
params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description).merge(spammable_params)
end