diff options
Diffstat (limited to 'app/controllers')
54 files changed, 291 insertions, 277 deletions
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 7f7d38a09c5..7c6a444ce7a 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -237,7 +237,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController [ *::ApplicationSettingsHelper.visible_attributes, *::ApplicationSettingsHelper.external_authorization_service_attributes, - *ApplicationSetting.repository_storages_weighted_attributes, *ApplicationSetting.kroki_formats_attributes.keys.map { |key| "kroki_formats_#{key}".to_sym }, :lets_encrypt_notification_email, :lets_encrypt_terms_of_service_accepted, @@ -248,8 +247,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :default_branch_name, disabled_oauth_sign_in_sources: [], import_sources: [], - repository_storages: [], - restricted_visibility_levels: [] + restricted_visibility_levels: [], + repository_storages_weighted: {} ] end diff --git a/app/controllers/admin/instance_statistics_controller.rb b/app/controllers/admin/instance_statistics_controller.rb deleted file mode 100644 index 30891fcfe7c..00000000000 --- a/app/controllers/admin/instance_statistics_controller.rb +++ /dev/null @@ -1,18 +0,0 @@ -# frozen_string_literal: true - -class Admin::InstanceStatisticsController < Admin::ApplicationController - include Analytics::UniqueVisitsHelper - - before_action :check_feature_flag - - track_unique_visits :index, target_id: 'i_analytics_instance_statistics' - - feature_category :devops_reports - - def index - end - - def check_feature_flag - render_404 unless Feature.enabled?(:instance_statistics, default_enabled: true) - end -end diff --git a/app/controllers/admin/usage_trends_controller.rb b/app/controllers/admin/usage_trends_controller.rb new file mode 100644 index 00000000000..7073f71a1a8 --- /dev/null +++ b/app/controllers/admin/usage_trends_controller.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class Admin::UsageTrendsController < Admin::ApplicationController + include Analytics::UniqueVisitsHelper + + track_unique_visits :index, target_id: 'i_analytics_instance_statistics' + + feature_category :devops_reports + + def index + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5f14d95ffed..379da90827a 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -29,7 +29,6 @@ class ApplicationController < ActionController::Base before_action :validate_user_service_ticket! before_action :check_password_expiration, if: :html_request? before_action :ldap_security_check - around_action :sentry_context before_action :default_headers before_action :default_cache_headers before_action :add_gon_variables, if: :html_request? @@ -171,7 +170,12 @@ class ApplicationController < ActionController::Base end def log_exception(exception) - Gitlab::ErrorTracking.track_exception(exception) + # At this point, the controller already exits set_current_context around + # block. To maintain the context while handling error exception, we need to + # set the context again + set_current_context do + Gitlab::ErrorTracking.track_exception(exception) + end backtrace_cleaner = request.env["action_dispatch.backtrace_cleaner"] application_trace = ActionDispatch::ExceptionWrapper.new(backtrace_cleaner, exception).application_trace @@ -528,10 +532,6 @@ class ApplicationController < ActionController::Base .execute end - def sentry_context(&block) - Gitlab::ErrorTracking.with_context(current_user, &block) - end - def allow_gitaly_ref_name_caching ::Gitlab::GitalyClient.allow_ref_name_caching do yield diff --git a/app/controllers/concerns/boards_responses.rb b/app/controllers/concerns/boards_responses.rb index 6e6686f225c..7307b7b4f8f 100644 --- a/app/controllers/concerns/boards_responses.rb +++ b/app/controllers/concerns/boards_responses.rb @@ -35,7 +35,7 @@ module BoardsResponses end def authorize_read_list - authorize_action_for!(board, :read_list) + authorize_action_for!(board, :read_issue_board_list) end def authorize_read_issue @@ -54,7 +54,7 @@ module BoardsResponses end def authorize_admin_list - authorize_action_for!(board, :admin_list) + authorize_action_for!(board, :admin_issue_board_list) end def authorize_action_for!(resource, ability) diff --git a/app/controllers/concerns/check_rate_limit.rb b/app/controllers/concerns/check_rate_limit.rb new file mode 100644 index 00000000000..c4de3315e22 --- /dev/null +++ b/app/controllers/concerns/check_rate_limit.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# == CheckRateLimit +# +# Controller concern that checks if the rate limit for a given action is throttled by calling the +# Gitlab::ApplicationRateLimiter class. If the action is throttled for the current user, the request +# will be logged and an error message will be rendered with a Too Many Requests response status. +module CheckRateLimit + def check_rate_limit(key) + return unless rate_limiter.throttled?(key, scope: current_user, users_allowlist: rate_limit_users_allowlist) + + rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user) + render plain: _('This endpoint has been requested too many times. Try again later.'), status: :too_many_requests + end + + def rate_limiter + ::Gitlab::ApplicationRateLimiter + end + + def rate_limit_users_allowlist + Gitlab::CurrentSettings.current_application_settings.notes_create_limit_allowlist + end +end diff --git a/app/controllers/concerns/comment_and_close_flag.rb b/app/controllers/concerns/comment_and_close_flag.rb deleted file mode 100644 index e2f3272abbc..00000000000 --- a/app/controllers/concerns/comment_and_close_flag.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -module CommentAndCloseFlag - extend ActiveSupport::Concern - - included do - before_action do - push_frontend_feature_flag(:remove_comment_close_reopen, @group) - end - end -end diff --git a/app/controllers/concerns/multiple_boards_actions.rb b/app/controllers/concerns/multiple_boards_actions.rb index 5206f5759d8..85bb73463db 100644 --- a/app/controllers/concerns/multiple_boards_actions.rb +++ b/app/controllers/concerns/multiple_boards_actions.rb @@ -80,7 +80,7 @@ module MultipleBoardsActions end def authorize_admin_board! - return render_404 unless can?(current_user, :admin_board, parent) + return render_404 unless can?(current_user, :admin_issue_board, parent) end def serializer diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb index 036d95622ef..2d8168af2e3 100644 --- a/app/controllers/concerns/notes_actions.rb +++ b/app/controllers/concerns/notes_actions.rb @@ -3,6 +3,7 @@ module NotesActions include RendersNotes include Gitlab::Utils::StrongMemoize + include CheckRateLimit extend ActiveSupport::Concern # last_fetched_at is an integer number of microseconds, which is the same @@ -15,6 +16,7 @@ module NotesActions before_action :require_noteable!, only: [:index, :create] before_action :authorize_admin_note!, only: [:update, :destroy] before_action :note_project, only: [:create] + before_action -> { check_rate_limit(:notes_create) }, only: [:create] end def index @@ -31,9 +33,9 @@ module NotesActions # We know there's more data, so tell the frontend to poll again after 1ms set_polling_interval_header(interval: 1) if meta[:more] - # We might still want to investigate further adjusting ETag caching with paginated notes, but - # let's avoid ETag caching for now until we confirm the viability of paginated notes. - ::Gitlab::EtagCaching::Middleware.skip!(response) + # Only present an ETag for the empty response to ensure pagination works + # as expected + ::Gitlab::EtagCaching::Middleware.skip!(response) if notes.present? render json: meta.merge(notes: notes) end diff --git a/app/controllers/concerns/security_and_compliance_permissions.rb b/app/controllers/concerns/security_and_compliance_permissions.rb new file mode 100644 index 00000000000..104f3638bb7 --- /dev/null +++ b/app/controllers/concerns/security_and_compliance_permissions.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module SecurityAndCompliancePermissions + extend ActiveSupport::Concern + + included do + before_action :ensure_security_and_compliance_enabled! + end + + private + + def ensure_security_and_compliance_enabled! + render_404 unless can?(current_user, :access_security_and_compliance, project) + end +end diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb index b285faee9bc..9e861d2859d 100644 --- a/app/controllers/concerns/spammable_actions.rb +++ b/app/controllers/concerns/spammable_actions.rb @@ -2,6 +2,7 @@ module SpammableActions extend ActiveSupport::Concern + include Spam::Concerns::HasSpamActionResponseFields included do before_action :authorize_submit_spammable!, only: :mark_as_spam @@ -25,14 +26,20 @@ module SpammableActions respond_to do |format| format.html do + # NOTE: format.html is still used by issue create, and uses the legacy HAML + # `_recaptcha_form.html.haml` rendered via the `projects/issues/verify` template. render :verify end format.json do - locals = { spammable: spammable, script: false, has_submit: false } - recaptcha_html = render_to_string(partial: 'shared/recaptcha_form', formats: :html, locals: locals) + # format.json is used by all new Vue-based CAPTCHA implementations, which + # handle all of the CAPTCHA form rendering on the client via the Pajamas-based + # app/assets/javascripts/captcha/captcha_modal.vue - render json: { recaptcha_html: recaptcha_html } + # NOTE: "409 - Conflict" seems to be the most appropriate HTTP status code for a response + # which requires a CAPTCHA to be solved in order for the request to be resubmitted. + # See https://stackoverflow.com/q/26547466/25192 + render json: spam_action_response_fields(spammable), status: :conflict end end else @@ -58,7 +65,7 @@ module SpammableActions # After this newer GraphQL/JS API process is fully supported by the backend, we can remove the # check for the 'g-recaptcha-response' field and other HTML/HAML form-specific support. - captcha_response = params['g-recaptcha-response'] + captcha_response = params['g-recaptcha-response'] || params[:captcha_response] { request: request, diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb index 4014e4f0024..60ff0a12d0c 100644 --- a/app/controllers/concerns/wiki_actions.rb +++ b/app/controllers/concerns/wiki_actions.rb @@ -112,10 +112,11 @@ module WikiActions wiki_page_path(wiki, page) ) else + @error = response.message render 'shared/wikis/edit' end rescue WikiPage::PageChangedError, WikiPage::PageRenameError => e - @error = e + @error = e.message render 'shared/wikis/edit' end # rubocop:enable Gitlab/ModuleWithInstanceVariables diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb index 01dc0b1ab00..5a885349467 100644 --- a/app/controllers/dashboard/snippets_controller.rb +++ b/app/controllers/dashboard/snippets_controller.rb @@ -19,6 +19,7 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController .page(params[:page]) .inc_author .inc_projects_namespace_route + .inc_statistics return if redirect_out_of_range(@snippets) diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index 0ae326b5d94..782c8c293fd 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -52,7 +52,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController end def bulk_restore - TodoService.new.restore_todos(current_user.todos.for_ids(params[:ids]), current_user) + TodoService.new.restore_todos(current_user.todos.id_in(params[:ids]), current_user) render json: todos_counts end diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index 1fd3cfd11f9..f6671f7250f 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -97,7 +97,7 @@ class Explore::ProjectsController < Explore::ApplicationController end def default_sort_order - sort_value_name + sort_value_latest_activity end def sorting_field diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb index 91ab18f2f55..617cc2e7f3d 100644 --- a/app/controllers/explore/snippets_controller.rb +++ b/app/controllers/explore/snippets_controller.rb @@ -11,6 +11,7 @@ class Explore::SnippetsController < Explore::ApplicationController .page(params[:page]) .without_count .inc_author + .inc_statistics @noteable_meta_data = noteable_meta_data(@snippets, 'Snippet') end diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index 152f07b4c16..53064041ab8 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -4,6 +4,8 @@ class GraphqlController < ApplicationController # Unauthenticated users have access to the API for public data skip_before_action :authenticate_user! + WHITELIST_HEADER = 'HTTP_X_GITLAB_QUERY_WHITELIST_ISSUE' + # If a user is using their session to access GraphQL, we need to have session # storage, since the admin-mode check is session wide. # We can't enable this for anonymous users because that would cause users using @@ -21,6 +23,7 @@ class GraphqlController < ApplicationController before_action(only: [:execute]) { authenticate_sessionless_user!(:api) } before_action :set_user_last_activity before_action :track_vs_code_usage + before_action :whitelist_query! # Since we deactivate authentication from the main ApplicationController and # defer it to :authorize_access_api!, we need to override the bypass session @@ -59,6 +62,14 @@ class GraphqlController < ApplicationController private + # Tests may mark some queries as exempt from query limits + def whitelist_query! + whitelist_issue = request.headers[WHITELIST_HEADER] + return unless whitelist_issue + + Gitlab::QueryLimiting.whitelist(whitelist_issue) + end + def set_user_last_activity return unless current_user @@ -66,7 +77,8 @@ class GraphqlController < ApplicationController end def track_vs_code_usage - Gitlab::UsageDataCounters::VSCodeExtensionActivityUniqueCounter.track_api_request_when_trackable(user_agent: request.user_agent, user: current_user) + Gitlab::UsageDataCounters::VSCodeExtensionActivityUniqueCounter + .track_api_request_when_trackable(user_agent: request.user_agent, user: current_user) end def execute_multiplex diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb index fa109021b7d..3354c0a5c9f 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 :assign_endpoint_vars before_action do push_frontend_feature_flag(:graphql_board_lists, group, default_enabled: false) + push_frontend_feature_flag(:boards_filtered_search, group) end feature_category :boards @@ -21,13 +22,13 @@ class Groups::BoardsController < Groups::ApplicationController def boards_finder strong_memoize :boards_finder do - Boards::ListService.new(parent, current_user) + Boards::BoardsFinder.new(parent, current_user) end end def board_finder strong_memoize :board_finder do - Boards::ListService.new(parent, current_user, board_id: params[:id]) + Boards::BoardsFinder.new(parent, current_user, board_id: params[:id]) end end @@ -44,6 +45,6 @@ class Groups::BoardsController < Groups::ApplicationController end def authorize_read_board! - access_denied! unless can?(current_user, :read_board, group) + access_denied! unless can?(current_user, :read_issue_board, group) end end diff --git a/app/controllers/groups/dependency_proxy_for_containers_controller.rb b/app/controllers/groups/dependency_proxy_for_containers_controller.rb index 0f640397320..e2c104f88a4 100644 --- a/app/controllers/groups/dependency_proxy_for_containers_controller.rb +++ b/app/controllers/groups/dependency_proxy_for_containers_controller.rb @@ -16,7 +16,18 @@ class Groups::DependencyProxyForContainersController < Groups::ApplicationContro result = DependencyProxy::FindOrCreateManifestService.new(group, image, tag, token).execute if result[:status] == :success - send_upload(result[:manifest].file) + response.headers['Docker-Content-Digest'] = result[:manifest].digest + response.headers['Content-Length'] = result[:manifest].size + response.headers['Docker-Distribution-Api-Version'] = DependencyProxy::DISTRIBUTION_API_VERSION + response.headers['Etag'] = "\"#{result[:manifest].digest}\"" + content_type = result[:manifest].content_type + + send_upload( + result[:manifest].file, + proxy: true, + redirect_params: { query: { 'response-content-type' => content_type } }, + send_params: { type: content_type } + ) else render status: result[:http_status], json: result[:message] end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 9d7aebe4505..5de207857bb 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -30,7 +30,6 @@ class GroupsController < Groups::ApplicationController before_action do push_frontend_feature_flag(:vue_issuables_list, @group) - push_frontend_feature_flag(:vue_notification_dropdown, @group, default_enabled: :yaml) end before_action do diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index e995562f0c4..06906001ef0 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -2,6 +2,7 @@ class HelpController < ApplicationController skip_before_action :authenticate_user!, unless: :public_visibility_restricted? + skip_before_action :check_two_factor_requirement feature_category :not_owned layout 'help' diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb index 8c0414ad5da..ea67c76a8bc 100644 --- a/app/controllers/ide_controller.rb +++ b/app/controllers/ide_controller.rb @@ -5,10 +5,12 @@ class IdeController < ApplicationController include ClientsidePreviewCSP include StaticObjectExternalStorageCSP + include Gitlab::Utils::StrongMemoize before_action do push_frontend_feature_flag(:build_service_proxy) push_frontend_feature_flag(:schema_linting) + define_index_vars end feature_category :web_ide @@ -16,4 +18,26 @@ class IdeController < ApplicationController def index Gitlab::UsageDataCounters::WebIdeCounter.increment_views_count end + + private + + def define_index_vars + return unless project + + @branch = params[:branch] + @path = params[:path] + @merge_request = params[:merge_request_id] + + unless can?(current_user, :push_code, project) + @forked_project = ForkProjectsFinder.new(project, current_user: current_user).execute.first + end + end + + def project + strong_memoize(:project) do + next unless params[:project_id].present? + + Project.find_by_full_path(params[:project_id]) + end + end end diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb index ef32ba4d119..48635c933af 100644 --- a/app/controllers/import/bulk_imports_controller.rb +++ b/app/controllers/import/bulk_imports_controller.rb @@ -37,8 +37,13 @@ class Import::BulkImportsController < ApplicationController end def create - result = BulkImportService.new(current_user, create_params, credentials).execute - render json: result.to_json(only: [:id]) + response = BulkImportService.new(current_user, create_params, credentials).execute + + if response.success? + render json: response.payload.to_json(only: [:id]) + else + render json: { error: response.message }, status: response.http_status + end end def realtime_changes @@ -128,7 +133,7 @@ class Import::BulkImportsController < ApplicationController rescue Gitlab::UrlBlocker::BlockedUrlError => e clear_session_data - redirect_to new_group_path, alert: _('Specified URL cannot be used: "%{reason}"') % { reason: e.message } + redirect_to new_group_path(anchor: 'import-group-pane'), alert: _('Specified URL cannot be used: "%{reason}"') % { reason: e.message } end def allow_local_requests? @@ -151,7 +156,7 @@ class Import::BulkImportsController < ApplicationController }, status: :unprocessable_entity end format.html do - redirect_to new_group_path + redirect_to new_group_path(anchor: 'import-group-pane') end end end diff --git a/app/controllers/import/gitlab_groups_controller.rb b/app/controllers/import/gitlab_groups_controller.rb index f68b76a7b36..503b10f766b 100644 --- a/app/controllers/import/gitlab_groups_controller.rb +++ b/app/controllers/import/gitlab_groups_controller.rb @@ -10,7 +10,7 @@ class Import::GitlabGroupsController < ApplicationController def create unless file_is_valid?(group_params[:file]) - return redirect_back_or_default(options: { alert: s_('GroupImport|Unable to process group import file') }) + return redirect_to new_group_path(anchor: 'import-group-pane'), alert: s_('GroupImport|Unable to process group import file') end group_data = group_params.except(:file).merge( @@ -30,9 +30,8 @@ class Import::GitlabGroupsController < ApplicationController 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 } } - ) + redirect_to new_group_path(anchor: 'import-group-pane'), + alert: s_("GroupImport|Group could not be imported: %{errors}") % { errors: group.errors.full_messages.to_sentence } end end diff --git a/app/controllers/jira_connect/subscriptions_controller.rb b/app/controllers/jira_connect/subscriptions_controller.rb index 161280a05fc..3ff12f29f10 100644 --- a/app/controllers/jira_connect/subscriptions_controller.rb +++ b/app/controllers/jira_connect/subscriptions_controller.rb @@ -19,9 +19,6 @@ class JiraConnect::SubscriptionsController < JiraConnect::ApplicationController before_action :allow_rendering_in_iframe, only: :index before_action :verify_qsh_claim!, only: :index before_action :authenticate_user!, only: :create - before_action do - push_frontend_feature_flag(:new_jira_connect_ui, type: :development, default_enabled: :yaml) - end def index @subscriptions = current_jira_installation.subscriptions.preload_namespace_route diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb deleted file mode 100644 index 7d8c035c852..00000000000 --- a/app/controllers/notification_settings_controller.rb +++ /dev/null @@ -1,61 +0,0 @@ -# frozen_string_literal: true - -class NotificationSettingsController < ApplicationController - before_action :authenticate_user! - - feature_category :users - - def create - return render_404 unless can_read?(resource) - - @notification_setting = current_user.notification_settings_for(resource) - @saved = @notification_setting.update(notification_setting_params_for(resource)) - - render_response - end - - def update - @notification_setting = current_user.notification_settings.find(params[:id]) - @saved = @notification_setting.update(notification_setting_params_for(@notification_setting.source)) - - render_response - end - - private - - def resource - @resource ||= - if params[:project_id].present? - Project.find(params[:project_id]) - elsif params[:namespace_id].present? - Group.find(params[:namespace_id]) - end - end - - def can_read?(resource) - ability_name = resource.class.name.downcase - ability_name = "read_#{ability_name}".to_sym - - can?(current_user, ability_name, resource) - end - - def render_response - btn_class = nil - - if params[:hide_label].present? - btn_class = 'btn-xs' if params[:project_id].present? - response_template = 'shared/notifications/_new_button' - else - response_template = 'shared/notifications/_button' - end - - render json: { - html: view_to_html_string(response_template, notification_setting: @notification_setting, btn_class: btn_class), - saved: @saved - } - end - - def notification_setting_params_for(source) - params.require(:notification_setting).permit(NotificationSetting.allowed_fields(source)) - end -end diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb index a45205c5da7..251967a7dff 100644 --- a/app/controllers/profiles/personal_access_tokens_controller.rb +++ b/app/controllers/profiles/personal_access_tokens_controller.rb @@ -3,6 +3,10 @@ class Profiles::PersonalAccessTokensController < Profiles::ApplicationController feature_category :authentication_and_authorization + before_action do + push_frontend_feature_flag(:personal_access_tokens_scoped_to_projects, current_user) + end + def index set_index_vars @personal_access_token = finder.build diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb index add5046e213..45bab5f6cd1 100644 --- a/app/controllers/profiles/preferences_controller.rb +++ b/app/controllers/profiles/preferences_controller.rb @@ -49,7 +49,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController :tab_width, :sourcegraph_enabled, :gitpod_enabled, - :render_whitespace_in_code + :render_whitespace_in_code, + :markdown_surround_selection ] end end diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 3bb00978aac..0080ae1a5be 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -31,9 +31,7 @@ class Projects::BlobController < Projects::ApplicationController before_action :editor_variables, except: [:show, :preview, :diff] before_action :validate_diff_params, only: :diff before_action :set_last_commit_sha, only: [:edit, :update] - before_action only: :new do - record_experiment_user(:ci_syntax_templates, namespace_id: @project.namespace_id) if params[:file_name] == @project.ci_config_path_or_default - end + before_action :record_experiment, only: :new track_redis_hll_event :create, :update, name: 'g_edit_by_sfe' @@ -263,4 +261,10 @@ class Projects::BlobController < Projects::ApplicationController def visitor_id current_user&.id end + + def record_experiment + return unless params[:file_name] == @project.ci_config_path_or_default && @project.namespace.recent? + + record_experiment_user(:ci_syntax_templates_b, namespace_id: @project.namespace_id) + end end diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb index d2e5d319f96..418f2ee1592 100644 --- a/app/controllers/projects/boards_controller.rb +++ b/app/controllers/projects/boards_controller.rb @@ -21,13 +21,13 @@ class Projects::BoardsController < Projects::ApplicationController def boards_finder strong_memoize :boards_finder do - Boards::ListService.new(parent, current_user) + Boards::BoardsFinder.new(parent, current_user) end end def board_finder strong_memoize :board_finder do - Boards::ListService.new(parent, current_user, board_id: params[:id]) + Boards::BoardsFinder.new(parent, current_user, board_id: params[:id]) end end @@ -45,6 +45,6 @@ class Projects::BoardsController < Projects::ApplicationController end def authorize_read_board! - access_denied! unless can?(current_user, :read_board, project) + access_denied! unless can?(current_user, :read_issue_board, project) 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 aabcb74cefa..fee216da492 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 @@ -1,11 +1,6 @@ # frozen_string_literal: true class Projects::Ci::DailyBuildGroupReportResultsController < Projects::ApplicationController - include Gitlab::Utils::StrongMemoize - - MAX_ITEMS = 1000 - REPORT_WINDOW = 90.days - before_action :authorize_read_build_report_results! before_action :validate_param_type! @@ -40,53 +35,23 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati end def report_results - if ::Gitlab::Ci::Features.use_coverage_data_new_finder?(project) - ::Ci::Testing::DailyBuildGroupReportResultsFinder.new( - params: new_finder_params, - current_user: current_user - ).execute - else - Ci::DailyBuildGroupReportResultsFinder.new(**finder_params).execute - end + ::Ci::DailyBuildGroupReportResultsFinder.new( + params: finder_params, + current_user: current_user + ).execute end - def new_finder_params + def finder_params { project: project, coverage: true, - start_date: start_date, - end_date: end_date, + start_date: params[:start_date], + end_date: params[:end_date], ref_path: params[:ref_path], sort: true } end - def finder_params - { - current_user: current_user, - project: project, - ref_path: params.require(:ref_path), - start_date: start_date, - end_date: end_date, - limit: MAX_ITEMS - } - end - - def start_date - strong_memoize(:start_date) do - start_date = Date.parse(params.require(:start_date)) - - # The start_date cannot be older than `end_date - 90 days` - [start_date, end_date - REPORT_WINDOW].max - end - end - - def end_date - strong_memoize(:end_date) do - Date.parse(params.require(:end_date)) - end - end - def allowed_param_types Ci::DailyBuildGroupReportResult::PARAM_TYPES end diff --git a/app/controllers/projects/ci/pipeline_editor_controller.rb b/app/controllers/projects/ci/pipeline_editor_controller.rb index 3552915b561..4d491b33aa0 100644 --- a/app/controllers/projects/ci/pipeline_editor_controller.rb +++ b/app/controllers/projects/ci/pipeline_editor_controller.rb @@ -5,12 +5,13 @@ class Projects::Ci::PipelineEditorController < Projects::ApplicationController before_action do push_frontend_feature_flag(:ci_config_visualization_tab, @project, default_enabled: :yaml) push_frontend_feature_flag(:ci_config_merged_tab, @project, default_enabled: :yaml) + push_frontend_feature_flag(:pipeline_status_for_pipeline_editor, @project, default_enabled: :yaml) + push_frontend_feature_flag(:pipeline_editor_empty_state_action, @project, default_enabled: :yaml) end feature_category :pipeline_authoring def show - render_404 unless ::Gitlab::Ci::Features.ci_pipeline_editor_page_enabled?(@project) end private diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index ffdd9fca95b..8154128da29 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -16,10 +16,16 @@ class Projects::CommitController < Projects::ApplicationController before_action :authorize_read_pipeline!, only: [:pipelines] before_action :commit before_action :define_commit_vars, only: [:show, :diff_for_path, :diff_files, :pipelines, :merge_requests] + before_action :define_commit_box_vars, only: [:show, :pipelines] before_action :define_note_vars, only: [:show, :diff_for_path, :diff_files] before_action :authorize_edit_tree!, only: [:revert, :cherry_pick] - before_action only: [:pipelines] do - push_frontend_feature_flag(:ci_mini_pipeline_gl_dropdown, @project, type: :development, default_enabled: :yaml) + + before_action only: [:show, :pipelines] do + push_frontend_feature_flag(:ci_commit_pipeline_mini_graph_vue, @project, default_enabled: :yaml) + end + + before_action do + push_frontend_feature_flag(:pick_into_project) end BRANCH_SEARCH_LIMIT = 1000 @@ -53,6 +59,8 @@ class Projects::CommitController < Projects::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def pipelines + set_pipeline_feature_flag + @pipelines = @commit.pipelines.order(id: :desc) @pipelines = @pipelines.where(ref: params[:ref]).page(params[:page]).per(30) if params[:ref] @@ -127,6 +135,10 @@ class Projects::CommitController < Projects::ApplicationController private + def set_pipeline_feature_flag + push_frontend_feature_flag(:new_pipelines_table, @project, default_enabled: :yaml) + end + def create_new_branch? params[:create_merge_request].present? || !can?(current_user, :push_code, @project) end @@ -199,6 +211,15 @@ class Projects::CommitController < Projects::ApplicationController end # rubocop: enable CodeReuse/ActiveRecord + def define_commit_box_vars + @last_pipeline = @commit.last_pipeline + + return unless ::Gitlab::Ci::Features.ci_commit_pipeline_mini_graph_vue_enabled?(@project) + return unless @commit.last_pipeline + + @last_pipeline_stages = StageSerializer.new(project: @project, current_user: @current_user).represent(@last_pipeline.stages) + end + def assign_change_commit_vars @start_branch = params[:start_branch] @commit_params = { commit: @commit } diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index 6be0b465402..81f80d37662 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -6,6 +6,7 @@ class Projects::CompareController < Projects::ApplicationController include DiffForPath include DiffHelper include RendersCommits + include CompareHelper # Authorize before_action :require_non_empty_project @@ -19,6 +20,10 @@ class Projects::CompareController < Projects::ApplicationController # Validation before_action :validate_refs! + before_action do + push_frontend_feature_flag(:compare_repo_dropdown, source_project, default_enabled: :yaml) + end + feature_category :source_code_management def index @@ -37,16 +42,18 @@ class Projects::CompareController < Projects::ApplicationController end def create - if params[:from].blank? || params[:to].blank? + from_to_vars = { + from: params[:from].presence, + to: params[:to].presence, + from_project_id: params[:from_project_id].presence + } + + if from_to_vars[:from].blank? || from_to_vars[:to].blank? flash[:alert] = "You must select a Source and a Target revision" - from_to_vars = { - from: params[:from].presence, - to: params[:to].presence - } - redirect_to project_compare_index_path(@project, from_to_vars) + + redirect_to project_compare_index_path(source_project, from_to_vars) else - redirect_to project_compare_path(@project, - params[:from], params[:to]) + redirect_to project_compare_path(source_project, from_to_vars) end end @@ -73,13 +80,34 @@ class Projects::CompareController < Projects::ApplicationController return if valid.all? flash[:alert] = "Invalid branch name" - redirect_to project_compare_index_path(@project) + redirect_to project_compare_index_path(source_project) + end + + # target == start_ref == from + def target_project + strong_memoize(:target_project) do + next source_project unless params.key?(:from_project_id) + next source_project unless Feature.enabled?(:compare_repo_dropdown, source_project, default_enabled: :yaml) + next source_project if params[:from_project_id].to_i == source_project.id + + target_project = target_projects(source_project).find_by_id(params[:from_project_id]) + + # Just ignore the field if it points at a non-existent or hidden project + next source_project unless target_project && can?(current_user, :download_code, target_project) + + target_project + end + end + + # source == head_ref == to + def source_project + project end def compare return @compare if defined?(@compare) - @compare = CompareService.new(@project, head_ref).execute(@project, start_ref) + @compare = CompareService.new(source_project, head_ref).execute(target_project, start_ref) end def start_ref @@ -102,9 +130,9 @@ class Projects::CompareController < Projects::ApplicationController def define_environment if compare - environment_params = @repository.branch_exists?(head_ref) ? { ref: head_ref } : { commit: compare.commit } + environment_params = source_project.repository.branch_exists?(head_ref) ? { ref: head_ref } : { commit: compare.commit } environment_params[:find_latest] = true - @environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last + @environment = EnvironmentsFinder.new(source_project, current_user, environment_params).execute.last end end @@ -114,8 +142,8 @@ class Projects::CompareController < Projects::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def merge_request - @merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened - .find_by(source_project: @project, source_branch: head_ref, target_branch: start_ref) + @merge_request ||= MergeRequestsFinder.new(current_user, project_id: target_project.id).execute.opened + .find_by(source_project: source_project, source_branch: head_ref, target_branch: start_ref) end # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index 5576d5766c7..33f046f414f 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -16,6 +16,10 @@ class Projects::ForksController < Projects::ApplicationController feature_category :source_code_management + before_action do + push_frontend_feature_flag(:fork_project_form) + end + def index @total_forks_count = project.forks.size @public_forks_count = project.forks.public_only.size diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 2b030793c58..ad39b317b31 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -62,7 +62,7 @@ class Projects::GraphsController < Projects::ApplicationController return unless can?(current_user, :read_build_report_results, project) date_today = Date.current - report_window = Projects::Ci::DailyBuildGroupReportResultsController::REPORT_WINDOW + report_window = ::Ci::DailyBuildGroupReportResultsFinder::REPORT_WINDOW @daily_coverage_options = { base_params: { diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 2816977277a..c454ae6eaf4 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -9,7 +9,6 @@ class Projects::IssuesController < Projects::ApplicationController include IssuesCalendar include SpammableActions include RecordUserLastActivity - include CommentAndCloseFlag ISSUES_EXCEPT_ACTIONS = %i[index calendar new create bulk_update import_csv export_csv service_desk].freeze SET_ISSUEABLES_INDEX_ONLY_ACTIONS = %i[index calendar service_desk].freeze @@ -45,6 +44,7 @@ class Projects::IssuesController < Projects::ApplicationController push_frontend_feature_flag(:tribute_autocomplete, @project) push_frontend_feature_flag(:vue_issuables_list, project) push_frontend_feature_flag(:usage_data_design_action, project, default_enabled: true) + push_frontend_feature_flag(:improved_emoji_picker, project, default_enabled: :yaml) end before_action only: :show do @@ -54,7 +54,6 @@ class Projects::IssuesController < Projects::ApplicationController push_to_gon_attributes(:features, real_time_feature_flag, real_time_enabled) push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml) - record_experiment_user(:invite_members_version_a) record_experiment_user(:invite_members_version_b) end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index c9e9a34ad88..2c6d5f62b4e 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -11,7 +11,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo include RecordUserLastActivity include SourcegraphDecorator include DiffHelper - include CommentAndCloseFlag skip_before_action :merge_request, only: [:index, :bulk_update, :export_csv] before_action :apply_diff_view_cookie!, only: [:show] @@ -33,7 +32,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:file_identifier_hash) push_frontend_feature_flag(:batch_suggestions, @project, default_enabled: true) push_frontend_feature_flag(:approvals_commented_by, @project, default_enabled: true) - push_frontend_feature_flag(:merge_request_widget_graphql, @project) + push_frontend_feature_flag(:merge_request_widget_graphql, @project, default_enabled: :yaml) push_frontend_feature_flag(:drag_comment_selection, @project, default_enabled: true) push_frontend_feature_flag(:unified_diff_components, @project, default_enabled: true) push_frontend_feature_flag(:default_merge_ref_for_diffs, @project, default_enabled: :yaml) @@ -41,12 +40,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:remove_resolve_note, @project, default_enabled: true) push_frontend_feature_flag(:diffs_gradual_load, @project, default_enabled: true) push_frontend_feature_flag(:codequality_backend_comparison, @project, default_enabled: :yaml) - push_frontend_feature_flag(:suggestions_custom_commit, @project, default_enabled: true) push_frontend_feature_flag(:local_file_reviews, default_enabled: :yaml) push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml) - push_frontend_feature_flag(:ci_mini_pipeline_gl_dropdown, @project, type: :development, default_enabled: :yaml) + push_frontend_feature_flag(:new_pipelines_table, @project, default_enabled: :yaml) - record_experiment_user(:invite_members_version_a) record_experiment_user(:invite_members_version_b) end diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 71a93701dc4..77fd7688caf 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -10,7 +10,6 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_read_note! before_action :authorize_create_note!, only: [:create] before_action :authorize_resolve_note!, only: [:resolve, :unresolve] - before_action :create_rate_limit, only: [:create] feature_category :issue_tracking @@ -91,20 +90,4 @@ class Projects::NotesController < Projects::ApplicationController def whitelist_query_limiting Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42383') end - - def create_rate_limit - key = :notes_create - return unless rate_limiter.throttled?(key, scope: [current_user], users_allowlist: rate_limit_users_allowlist) - - rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user) - render plain: _('This endpoint has been requested too many times. Try again later.'), status: :too_many_requests - end - - def rate_limiter - ::Gitlab::ApplicationRateLimiter - end - - def rate_limit_users_allowlist - Gitlab::CurrentSettings.current_application_settings.notes_create_limit_allowlist - end end diff --git a/app/controllers/projects/pipelines/tests_controller.rb b/app/controllers/projects/pipelines/tests_controller.rb index 1702783b10f..25ec7ab1335 100644 --- a/app/controllers/projects/pipelines/tests_controller.rb +++ b/app/controllers/projects/pipelines/tests_controller.rb @@ -32,7 +32,7 @@ module Projects # rubocop: disable CodeReuse/ActiveRecord def builds - @builds ||= pipeline.latest_builds.for_ids(build_ids).presence || render_404 + @builds ||= pipeline.latest_builds.id_in(build_ids).presence || render_404 end def build_ids diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 59b14bbb91d..9b5f5871c41 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -13,12 +13,11 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :authorize_create_pipeline!, only: [:new, :create, :config_variables] before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action do - push_frontend_feature_flag(:pipelines_security_report_summary, project) - push_frontend_feature_flag(:new_pipeline_form, project, default_enabled: true) + push_frontend_feature_flag(:new_pipeline_form, project, default_enabled: :yaml) push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: :yaml) push_frontend_feature_flag(:graphql_pipeline_details_users, current_user, type: :development, default_enabled: :yaml) - push_frontend_feature_flag(:ci_mini_pipeline_gl_dropdown, project, type: :development, default_enabled: :yaml) push_frontend_feature_flag(:jira_for_vulnerabilities, project, type: :development, default_enabled: :yaml) + push_frontend_feature_flag(:new_pipelines_table, project, default_enabled: :yaml) end before_action :ensure_pipeline, only: [:show] diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb index a7c7839dc9f..5972b29a298 100644 --- a/app/controllers/projects/project_members_controller.rb +++ b/app/controllers/projects/project_members_controller.rb @@ -8,10 +8,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController # Authorize before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access] - before_action do - push_frontend_feature_flag(:vue_project_members_list, @project, default_enabled: :yaml) - end - feature_category :authentication_and_authorization def index diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb index d4f7d0bc521..84b155c8002 100644 --- a/app/controllers/projects/protected_branches_controller.rb +++ b/app/controllers/projects/protected_branches_controller.rb @@ -21,6 +21,7 @@ class Projects::ProtectedBranchesController < Projects::ProtectedRefsController def protected_ref_params(*attrs) attrs = ([:name, + :allow_force_push, merge_access_levels_attributes: access_level_attributes, push_access_levels_attributes: access_level_attributes] + attrs).uniq diff --git a/app/controllers/projects/security/configuration_controller.rb b/app/controllers/projects/security/configuration_controller.rb index 9366ca7b0ed..bc4e58e54a9 100644 --- a/app/controllers/projects/security/configuration_controller.rb +++ b/app/controllers/projects/security/configuration_controller.rb @@ -3,19 +3,13 @@ module Projects module Security class ConfigurationController < Projects::ApplicationController + include SecurityAndCompliancePermissions + feature_category :static_application_security_testing def show - return render_404 unless feature_enabled? - render_403 unless can?(current_user, :read_security_configuration, project) end - - private - - def feature_enabled? - ::Feature.enabled?(:secure_security_and_compliance_configuration_page_on_ce, @project, default_enabled: :yaml) - end end end end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index b5c73f29784..554eb01defe 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -12,8 +12,6 @@ class Projects::ServicesController < Projects::ApplicationController 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(:jira_issues_integration, @project, type: :licensed, default_enabled: true) - push_frontend_feature_flag(:jira_vulnerabilities_integration, @project, type: :licensed, default_enabled: true) push_frontend_feature_flag(:jira_for_vulnerabilities, @project, type: :development, default_enabled: :yaml) end diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb index f8155b77e60..c407b15e29f 100644 --- a/app/controllers/projects/settings/operations_controller.rb +++ b/app/controllers/projects/settings/operations_controller.rb @@ -6,10 +6,6 @@ module Projects before_action :authorize_admin_operations! before_action :authorize_read_prometheus_alerts!, only: [:reset_alerting_token] - before_action do - push_frontend_feature_flag(:multiple_http_integrations_custom_mapping, @project) - end - respond_to :json, only: [:reset_alerting_token, :reset_pagerduty_token] helper_method :error_tracking_setting diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 779e149bb9c..ff28c3be298 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -22,6 +22,7 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController .execute .page(params[:page]) .inc_author + .inc_statistics return if redirect_out_of_range(@snippets) diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb index ab05c9694fd..b4b8fb97049 100644 --- a/app/controllers/projects/templates_controller.rb +++ b/app/controllers/projects/templates_controller.rb @@ -25,7 +25,7 @@ class Projects::TemplatesController < Projects::ApplicationController def names respond_to do |format| - format.json { render json: TemplateFinder.all_template_names_array(project, params[:template_type].to_s.pluralize) } + format.json { render json: TemplateFinder.all_template_names_hash_or_array(project, params[:template_type].to_s) } end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index ebffb62cff3..bc48ebd1c74 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -31,10 +31,6 @@ class ProjectsController < Projects::ApplicationController # Project Export Rate Limit before_action :export_rate_limit, only: [:export, :download_export, :generate_new_export] - before_action do - push_frontend_feature_flag(:vue_notification_dropdown, @project, default_enabled: :yaml) - end - before_action only: [:edit] do push_frontend_feature_flag(:allow_editing_commit_messages, @project) end @@ -74,7 +70,11 @@ class ProjectsController < Projects::ApplicationController @project = ::Projects::CreateService.new(current_user, project_params(attributes: project_params_create_attributes)).execute if @project.saved? - experiment(:new_project_readme, actor: current_user).track(:created, property: active_new_project_tab) + experiment(:new_project_readme, actor: current_user).track( + :created, + property: active_new_project_tab, + value: project_params[:initialize_with_readme].to_i + ) redirect_to( project_path(@project, custom_import_params), notice: _("Project '%{project_name}' was successfully created.") % { project_name: @project.name } @@ -90,21 +90,13 @@ class ProjectsController < Projects::ApplicationController # Refresh the repo in case anything changed @repository = @project.repository - respond_to do |format| - if result[:status] == :success - flash[:notice] = _("Project '%{project_name}' was successfully updated.") % { project_name: @project.name } - - format.html do - redirect_to(edit_project_path(@project, anchor: 'js-general-project-settings')) - end - else - flash[:alert] = result[:message] - @project.reset - - format.html { render_edit } - end - - format.js + if result[:status] == :success + flash[:notice] = _("Project '%{project_name}' was successfully updated.") % { project_name: @project.name } + redirect_to(edit_project_path(@project, anchor: 'js-general-project-settings')) + else + flash[:alert] = result[:message] + @project.reset + render 'edit' end end @@ -329,6 +321,11 @@ class ProjectsController < Projects::ApplicationController if can?(current_user, :download_code, @project) return render 'projects/no_repo' unless @project.repository_exists? + if @project.can_current_user_push_to_default_branch? + property = @project.empty_repo? ? 'empty' : 'nonempty' + experiment(:empty_repo_upload, project: @project).track(:view_project_show, property: property) + end + if @project.empty_repo? record_experiment_user(:invite_members_empty_project_version_a) @@ -393,6 +390,7 @@ class ProjectsController < Projects::ApplicationController metrics_dashboard_access_level analytics_access_level operations_access_level + security_and_compliance_access_level ] end @@ -523,7 +521,7 @@ class ProjectsController < Projects::ApplicationController def export_rate_limit prefixed_action = "project_#{params[:action]}".to_sym - project_scope = params[:action] == :download_export ? @project : nil + 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) diff --git a/app/controllers/repositories/git_http_controller.rb b/app/controllers/repositories/git_http_controller.rb index 9ad700404ff..d68ba80ab5d 100644 --- a/app/controllers/repositories/git_http_controller.rb +++ b/app/controllers/repositories/git_http_controller.rb @@ -78,11 +78,11 @@ module Repositories def update_fetch_statistics return unless project return if Gitlab::Database.read_only? - return if Feature.enabled?(:disable_git_http_fetch_writes) - return unless repo_type.project? - OnboardingProgressService.new(project.namespace).execute(action: :git_read) + OnboardingProgressService.async(project.namespace_id).execute(action: :git_pull) + + return if Feature.enabled?(:disable_git_http_fetch_writes) if Feature.enabled?(:project_statistics_sync, project, default_enabled: true) Projects::FetchStatisticsIncrementService.new(project).execute diff --git a/app/controllers/root_controller.rb b/app/controllers/root_controller.rb index 191134472c2..dab2f3bd67a 100644 --- a/app/controllers/root_controller.rb +++ b/app/controllers/root_controller.rb @@ -46,6 +46,8 @@ class RootController < Dashboard::ProjectsController redirect_to(activity_dashboard_path) when 'starred_project_activity' redirect_to(activity_dashboard_path(filter: 'starred')) + when 'followed_user_activity' + redirect_to(activity_dashboard_path(filter: 'followed')) when 'groups' redirect_to(dashboard_groups_path) when 'todos' @@ -69,7 +71,7 @@ class RootController < Dashboard::ProjectsController end def customize_homepage - @customize_homepage = experiment_enabled?(:customize_homepage) + @customize_homepage = Feature.enabled?(:customize_homepage, default_enabled: :yaml) end end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 820b00a902e..45c1c35a655 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -49,6 +49,12 @@ class SearchController < ApplicationController scope = search_service.scope count = search_service.search_results.formatted_count(scope) + # Users switching tabs will keep fetching the same tab counts so it's a + # good idea to cache in their browser just for a short time. They can still + # clear cache if they are seeing an incorrect count but inaccurate count is + # not such a bad thing. + expires_in 1.minute + render json: { count: count } end @@ -125,7 +131,7 @@ class SearchController < ApplicationController payload[:metadata] ||= {} payload[:metadata]['meta.search.group_id'] = params[:group_id] payload[:metadata]['meta.search.project_id'] = params[:project_id] - payload[:metadata]['meta.search.scope'] = params[:scope] + payload[:metadata]['meta.search.scope'] = params[:scope] || @scope payload[:metadata]['meta.search.filters.confidential'] = params[:confidential] payload[:metadata]['meta.search.filters.state'] = params[:state] payload[:metadata]['meta.search.force_search_results'] = params[:force_search_results] diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 913b1e3bb6e..1c6168dbc2c 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -24,6 +24,7 @@ class SnippetsController < Snippets::ApplicationController .execute .page(params[:page]) .inc_author + .inc_statistics return if redirect_out_of_range(@snippets) diff --git a/app/controllers/user_callouts_controller.rb b/app/controllers/user_callouts_controller.rb index cfec9d6d905..df3e2425e9f 100644 --- a/app/controllers/user_callouts_controller.rb +++ b/app/controllers/user_callouts_controller.rb @@ -4,10 +4,11 @@ class UserCalloutsController < ApplicationController feature_category :navigation def create - callout = ensure_callout + callout = Users::DismissUserCalloutService.new( + container: nil, current_user: current_user, params: { feature_name: feature_name } + ).execute if callout.persisted? - callout.update(dismissed_at: Time.current) respond_to do |format| format.json { head :ok } end @@ -20,12 +21,6 @@ class UserCalloutsController < ApplicationController private - # rubocop: disable CodeReuse/ActiveRecord - def ensure_callout - current_user.callouts.find_or_create_by(feature_name: UserCallout.feature_names[feature_name]) - end - # rubocop: enable CodeReuse/ActiveRecord - def feature_name params.require(:feature_name) end |