summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-04-25 03:09:02 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2023-04-25 03:09:02 +0000
commit26891eec2cea8ca5e75a96a50d8785da6042cc5a (patch)
treeb732a7a69a0a4cf44afa24bf9d6eddfa5c3805fd /app
parent15e5a05bcd3525dd6c046dca2682b04532ba9bd1 (diff)
downloadgitlab-ce-26891eec2cea8ca5e75a96a50d8785da6042cc5a.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/profile/preferences/components/profile_preferences.vue4
-rw-r--r--app/controllers/concerns/product_analytics_tracking.rb20
-rw-r--r--app/controllers/concerns/redis_tracking.rb49
-rw-r--r--app/controllers/concerns/snippets_actions.rb4
-rw-r--r--app/controllers/concerns/wiki_actions.rb4
-rw-r--r--app/controllers/profiles/preferences_controller.rb4
-rw-r--r--app/controllers/projects/pipelines_controller.rb12
-rw-r--r--app/controllers/search_controller.rb2
-rw-r--r--app/controllers/users_controller.rb7
-rw-r--r--app/models/user.rb61
-rw-r--r--app/services/users/update_service.rb29
-rw-r--r--app/views/profiles/preferences/show.html.haml16
-rw-r--r--app/views/users/show.html.haml2
13 files changed, 123 insertions, 91 deletions
diff --git a/app/assets/javascripts/profile/preferences/components/profile_preferences.vue b/app/assets/javascripts/profile/preferences/components/profile_preferences.vue
index d0d947ddd6e..164ec46cdb9 100644
--- a/app/assets/javascripts/profile/preferences/components/profile_preferences.vue
+++ b/app/assets/javascripts/profile/preferences/components/profile_preferences.vue
@@ -131,6 +131,10 @@ export default {
:message-url="view.message_url"
:config="$options.integrationViewConfigs[view.name]"
/>
+ </div>
+
+ <div class="col-lg-4"></div>
+ <div class="col-lg-8">
<hr />
</div>
<div class="col-sm-12 js-hide-when-nothing-matches-search">
diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb
index fc33770b4d8..5424354b92c 100644
--- a/app/controllers/concerns/product_analytics_tracking.rb
+++ b/app/controllers/concerns/product_analytics_tracking.rb
@@ -2,7 +2,6 @@
module ProductAnalyticsTracking
include Gitlab::Tracking::Helpers
- include RedisTracking
extend ActiveSupport::Concern
class_methods do
@@ -39,4 +38,23 @@ module ProductAnalyticsTracking
**optional_arguments
)
end
+
+ def track_unique_redis_hll_event(event_name, &block)
+ custom_id = block ? yield(self) : nil
+
+ unique_id = custom_id || visitor_id
+
+ return unless unique_id
+
+ Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: unique_id)
+ end
+
+ def visitor_id
+ return cookies[:visitor_id] if cookies[:visitor_id].present?
+ return unless current_user
+
+ uuid = SecureRandom.uuid
+ cookies[:visitor_id] = { value: uuid, expires: 24.months }
+ uuid
+ end
end
diff --git a/app/controllers/concerns/redis_tracking.rb b/app/controllers/concerns/redis_tracking.rb
deleted file mode 100644
index 445e72b8266..00000000000
--- a/app/controllers/concerns/redis_tracking.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-# Example:
-#
-# # In controller include module
-# # Track event for index action
-#
-# include RedisTracking
-#
-# track_redis_hll_event :index, :show, name: 'i_analytics_dev_ops_score'
-#
-# You can also pass custom conditions using `if:`, using the same format as with Rails callbacks.
-# You can also pass an optional block that calculates and returns a custom id to track.
-module RedisTracking
- include Gitlab::Tracking::Helpers
- extend ActiveSupport::Concern
-
- class_methods do
- def track_redis_hll_event(*controller_actions, name:, if: nil, &block)
- custom_conditions = Array.wrap(binding.local_variable_get('if'))
- conditions = [:trackable_html_request?, *custom_conditions]
-
- after_action only: controller_actions, if: conditions do
- track_unique_redis_hll_event(name, &block)
- end
- end
- end
-
- private
-
- def track_unique_redis_hll_event(event_name, &block)
- custom_id = block ? yield(self) : nil
-
- unique_id = custom_id || visitor_id
-
- return unless unique_id
-
- Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: unique_id)
- end
-
- def visitor_id
- return cookies[:visitor_id] if cookies[:visitor_id].present?
- return unless current_user
-
- uuid = SecureRandom.uuid
- cookies[:visitor_id] = { value: uuid, expires: 24.months }
- uuid
- end
-end
diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb
index 1bb81a46e50..62c5aee16e4 100644
--- a/app/controllers/concerns/snippets_actions.rb
+++ b/app/controllers/concerns/snippets_actions.rb
@@ -9,13 +9,13 @@ module SnippetsActions
include Gitlab::NoteableMetadata
include Snippets::SendBlob
include SnippetsSort
- include RedisTracking
+ include ProductAnalyticsTracking
included do
skip_before_action :verify_authenticity_token,
if: -> { action_name == 'show' && js_request? }
- track_redis_hll_event :show, name: 'i_snippets_show'
+ track_event :show, name: 'i_snippets_show'
respond_to :html
end
diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb
index a007abacacc..265cf2a7698 100644
--- a/app/controllers/concerns/wiki_actions.rb
+++ b/app/controllers/concerns/wiki_actions.rb
@@ -5,7 +5,7 @@ module WikiActions
include PreviewMarkdown
include SendsBlob
include Gitlab::Utils::StrongMemoize
- include RedisTracking
+ include ProductAnalyticsTracking
extend ActiveSupport::Concern
RESCUE_GIT_TIMEOUTS_IN = %w[show edit history diff pages].freeze
@@ -46,7 +46,7 @@ module WikiActions
end
end
- track_redis_hll_event :show, name: 'wiki_action'
+ track_event :show, name: 'wiki_action'
helper_method :view_file_button, :diff_file_html_data
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index 7786bad4251..b7e8432c039 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -37,7 +37,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController
end
def preferences_param_names
- [
+ preferences_param_names = [
:color_scheme_id,
:diffs_deletion_color,
:diffs_addition_color,
@@ -60,6 +60,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:use_legacy_web_ide,
:use_new_navigation
]
+ preferences_param_names << :enabled_following if ::Feature.enabled?(:disable_follow_users, user)
+ preferences_param_names
end
end
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index a8107a46b4f..30f582f90a5 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -2,7 +2,7 @@
class Projects::PipelinesController < Projects::ApplicationController
include ::Gitlab::Utils::StrongMemoize
- include RedisTracking
+ include ProductAnalyticsTracking
include ProductAnalyticsTracking
include ProjectStatsRefreshConflictsGuard
@@ -34,11 +34,11 @@ class Projects::PipelinesController < Projects::ApplicationController
label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly',
destinations: %i[redis_hll snowplow]
- track_redis_hll_event :charts, name: 'p_analytics_ci_cd_pipelines', if: -> { should_track_ci_cd_pipelines? }
- track_redis_hll_event :charts, name: 'p_analytics_ci_cd_deployment_frequency', if: -> { should_track_ci_cd_deployment_frequency? }
- track_redis_hll_event :charts, name: 'p_analytics_ci_cd_lead_time', if: -> { should_track_ci_cd_lead_time? }
- track_redis_hll_event :charts, name: 'p_analytics_ci_cd_time_to_restore_service', if: -> { should_track_ci_cd_time_to_restore_service? }
- track_redis_hll_event :charts, name: 'p_analytics_ci_cd_change_failure_rate', if: -> { should_track_ci_cd_change_failure_rate? }
+ track_event :charts, name: 'p_analytics_ci_cd_pipelines', conditions: -> { should_track_ci_cd_pipelines? }
+ track_event :charts, name: 'p_analytics_ci_cd_deployment_frequency', conditions: -> { should_track_ci_cd_deployment_frequency? }
+ track_event :charts, name: 'p_analytics_ci_cd_lead_time', conditions: -> { should_track_ci_cd_lead_time? }
+ track_event :charts, name: 'p_analytics_ci_cd_time_to_restore_service', conditions: -> { should_track_ci_cd_time_to_restore_service? }
+ track_event :charts, name: 'p_analytics_ci_cd_change_failure_rate', conditions: -> { should_track_ci_cd_change_failure_rate? }
wrap_parameters Ci::Pipeline
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 688c56e56e0..a3c6499bc54 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -3,7 +3,7 @@
class SearchController < ApplicationController
include ControllerWithCrossProjectAccessCheck
include SearchHelper
- include RedisTracking
+ include ProductAnalyticsTracking
include ProductAnalyticsTracking
include SearchRateLimitable
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index e4354eaa452..02b29d42313 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -191,7 +191,12 @@ class UsersController < ApplicationController
def follow
followee = current_user.follow(user)
- flash[:alert] = followee.errors.full_messages.join(', ') if followee&.errors&.any?
+ if followee
+ flash[:alert] = followee.errors.full_messages.join(', ') if followee&.errors&.any?
+ else
+ flash[:alert] = s_('Action not allowed.')
+ end
+
redirect_path = referer_path(request) || @user
redirect_to redirect_path
diff --git a/app/models/user.rb b/app/models/user.rb
index 0aa509e58d7..cb1fbd97a00 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -349,29 +349,30 @@ class User < ApplicationRecord
# User's role
enum role: { software_developer: 0, development_team_lead: 1, devops_engineer: 2, systems_administrator: 3, security_analyst: 4, data_analyst: 5, product_manager: 6, product_designer: 7, other: 8 }, _suffix: true
- delegate :notes_filter_for,
- :set_notes_filter,
- :first_day_of_week, :first_day_of_week=,
- :timezone, :timezone=,
- :time_display_relative, :time_display_relative=,
- :time_format_in_24h, :time_format_in_24h=,
- :show_whitespace_in_diffs, :show_whitespace_in_diffs=,
- :view_diffs_file_by_file, :view_diffs_file_by_file=,
- :pass_user_identities_to_ci_jwt, :pass_user_identities_to_ci_jwt=,
- :tab_width, :tab_width=,
- :sourcegraph_enabled, :sourcegraph_enabled=,
- :gitpod_enabled, :gitpod_enabled=,
- :setup_for_company, :setup_for_company=,
- :render_whitespace_in_code, :render_whitespace_in_code=,
- :markdown_surround_selection, :markdown_surround_selection=,
- :markdown_automatic_lists, :markdown_automatic_lists=,
- :diffs_deletion_color, :diffs_deletion_color=,
- :diffs_addition_color, :diffs_addition_color=,
- :use_legacy_web_ide, :use_legacy_web_ide=,
- :use_new_navigation, :use_new_navigation=,
- :pinned_nav_items, :pinned_nav_items=,
- :achievements_enabled, :achievements_enabled=,
- to: :user_preference
+ delegate :notes_filter_for,
+ :set_notes_filter,
+ :first_day_of_week, :first_day_of_week=,
+ :timezone, :timezone=,
+ :time_display_relative, :time_display_relative=,
+ :time_format_in_24h, :time_format_in_24h=,
+ :show_whitespace_in_diffs, :show_whitespace_in_diffs=,
+ :view_diffs_file_by_file, :view_diffs_file_by_file=,
+ :pass_user_identities_to_ci_jwt, :pass_user_identities_to_ci_jwt=,
+ :tab_width, :tab_width=,
+ :sourcegraph_enabled, :sourcegraph_enabled=,
+ :gitpod_enabled, :gitpod_enabled=,
+ :setup_for_company, :setup_for_company=,
+ :render_whitespace_in_code, :render_whitespace_in_code=,
+ :markdown_surround_selection, :markdown_surround_selection=,
+ :markdown_automatic_lists, :markdown_automatic_lists=,
+ :diffs_deletion_color, :diffs_deletion_color=,
+ :diffs_addition_color, :diffs_addition_color=,
+ :use_legacy_web_ide, :use_legacy_web_ide=,
+ :use_new_navigation, :use_new_navigation=,
+ :pinned_nav_items, :pinned_nav_items=,
+ :achievements_enabled, :achievements_enabled=,
+ :enabled_following, :enabled_following=,
+ to: :user_preference
delegate :path, to: :namespace, allow_nil: true, prefix: true
delegate :job_title, :job_title=, to: :user_detail, allow_nil: true
@@ -1694,7 +1695,7 @@ class User < ApplicationRecord
end
def follow(user)
- return false if self.id == user.id
+ return false unless following_users_allowed?(user)
begin
followee = Users::UserFollowUser.create(follower_id: self.id, followee_id: user.id)
@@ -1713,6 +1714,18 @@ class User < ApplicationRecord
end
end
+ def following_users_allowed?(user)
+ return false if self.id == user.id
+
+ following_users_enabled? && user.following_users_enabled?
+ end
+
+ def following_users_enabled?
+ return true unless ::Feature.enabled?(:disable_follow_users, self)
+
+ enabled_following
+ end
+
def forkable_namespaces
strong_memoize(:forkable_namespaces) do
personal_namespace = Namespace.where(id: namespace_id)
diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb
index 96018db5974..36c41c03303 100644
--- a/app/services/users/update_service.rb
+++ b/app/services/users/update_service.rb
@@ -6,6 +6,7 @@ module Users
attr_reader :user, :identity_params
ATTRS_REQUIRING_PASSWORD_CHECK = %w[email].freeze
+ BATCH_SIZE = 100
def initialize(current_user, params = {})
@current_user = current_user
@@ -34,7 +35,7 @@ module Users
reset_unconfirmed_email
if @user.save(validate: validate) && update_status
- notify_success(user_exists)
+ after_update(user_exists)
else
messages = @user.errors.full_messages + Array(@user.status&.errors&.full_messages)
error(messages.uniq.join('. '))
@@ -80,8 +81,6 @@ module Users
def notify_success(user_exists)
notify_new_user(@user, nil) unless user_exists
-
- success
end
def discard_read_only_attributes
@@ -118,6 +117,30 @@ module Users
def provider_params
identity_params.slice(*provider_attributes)
end
+
+ def after_update(user_exists)
+ notify_success(user_exists)
+ remove_followers_and_followee! if ::Feature.enabled?(:disable_follow_users, user)
+
+ success
+ end
+
+ def remove_followers_and_followee!
+ return false unless user.user_preference.enabled_following_previously_changed?(from: true, to: false)
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ loop do
+ inner_query = Users::UserFollowUser
+ .where(follower_id: user.id).or(Users::UserFollowUser.where(followee_id: user.id))
+ .select(:follower_id, :followee_id)
+ .limit(BATCH_SIZE)
+
+ deleted_records = Users::UserFollowUser.where('(follower_id, followee_id) IN (?)', inner_query).delete_all
+
+ break if deleted_records == 0
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
end
end
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index c16469bbf79..8fb66cb3cd9 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -170,5 +170,21 @@
- if Feature.enabled?(:user_time_settings)
.form-group
= f.gitlab_ui_checkbox_component :time_format_in_24h, s_('Preferences|Display time in 24-hour format')
+ - if Feature.enabled?(:disable_follow_users, @user)
+ .row.js-preferences-form.js-search-settings-section
+ .col-sm-12
+ %hr
+ .col-lg-4.profile-settings-sidebar#disabled_following
+ %h4.gl-mt-0
+ = s_('Preferences|Disable follow users feature')
+ %p
+ = s_('Preferences|Turns off the ability to follow or be followed by other users.')
+ = succeed '.' do
+ = link_to _('Learn more'), help_page_path('user/profile/index', anchor: 'follow-users'), target: '_blank', rel: 'noopener noreferrer'
+ .col-lg-8
+ .form-group
+ = f.gitlab_ui_checkbox_component :enabled_following,
+ s_('Preferences|Disable follow users')
+
#js-profile-preferences-app{ data: data_attributes }
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 70dccc4821b..e26b4de7b20 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -36,7 +36,7 @@
= render Pajamas::ButtonComponent.new(href: [:admin, @user],
icon: 'user',
button_options: { class: 'gl-flex-grow-1 gl-mx-1 has-tooltip', title: s_('UserProfile|View user in admin area'), data: {toggle: 'tooltip', placement: 'bottom', container: 'body'}})
- - if current_user && current_user.id != @user.id
+ - if current_user && current_user.following_users_allowed?(@user)
- if current_user.following?(@user)
= form_tag user_unfollow_path(@user, :json), class: link_classes + 'gl-display-inline-block' do
= render Pajamas::ButtonComponent.new(type: :submit, button_options: { class: 'gl-w-full', data: { track_action: 'click_button', track_label: 'unfollow_from_profile' } }) do