diff options
Diffstat (limited to 'app')
25 files changed, 141 insertions, 91 deletions
diff --git a/app/assets/javascripts/incidents_settings/components/incidents_settings_tabs.vue b/app/assets/javascripts/incidents_settings/components/incidents_settings_tabs.vue index 0623c275c5a..7411c0ffe0d 100644 --- a/app/assets/javascripts/incidents_settings/components/incidents_settings_tabs.vue +++ b/app/assets/javascripts/incidents_settings/components/incidents_settings_tabs.vue @@ -2,7 +2,6 @@ import { GlButton, GlTabs, GlTab } from '@gitlab/ui'; import AlertsSettingsForm from './alerts_form.vue'; import PagerDutySettingsForm from './pagerduty_form.vue'; -import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { INTEGRATION_TABS_CONFIG, I18N_INTEGRATION_TABS } from '../constants'; export default { @@ -13,17 +12,8 @@ export default { AlertsSettingsForm, PagerDutySettingsForm, }, - mixins: [glFeatureFlagMixin()], tabs: INTEGRATION_TABS_CONFIG, i18n: I18N_INTEGRATION_TABS, - methods: { - isFeatureFlagEnabled(tab) { - if (tab.featureFlag) { - return this.glFeatures[tab.featureFlag]; - } - return true; - }, - }, }; </script> @@ -49,7 +39,7 @@ export default { <gl-tabs> <gl-tab v-for="(tab, index) in $options.tabs" - v-if="tab.active && isFeatureFlagEnabled(tab)" + v-if="tab.active" :key="`${tab.title}_${index}`" :title="tab.title" > diff --git a/app/assets/javascripts/incidents_settings/constants.js b/app/assets/javascripts/incidents_settings/constants.js index b443c237f0f..e68198f84e0 100644 --- a/app/assets/javascripts/incidents_settings/constants.js +++ b/app/assets/javascripts/incidents_settings/constants.js @@ -11,7 +11,6 @@ export const INTEGRATION_TABS_CONFIG = [ title: s__('IncidentSettings|PagerDuty integration'), component: 'PagerDutySettingsForm', active: true, - featureFlag: 'pagerdutyWebhook', }, { title: s__('IncidentSettings|Grafana integration'), diff --git a/app/assets/javascripts/monitoring/components/refresh_button.vue b/app/assets/javascripts/monitoring/components/refresh_button.vue index 5481806c3e0..31092a26048 100644 --- a/app/assets/javascripts/monitoring/components/refresh_button.vue +++ b/app/assets/javascripts/monitoring/components/refresh_button.vue @@ -10,6 +10,7 @@ import { GlNewDropdownDivider, GlTooltipDirective, } from '@gitlab/ui'; +import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; const makeInterval = (length = 0, unit = 's') => { const shortLabel = `${length}${unit}`; @@ -53,6 +54,7 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, + mixins: [glFeatureFlagsMixin()], data() { return { refreshInterval: null, @@ -60,6 +62,12 @@ export default { }; }, computed: { + disableMetricDashboardRefreshRate() { + // Can refresh rates impact performance? + // Add "negative" feature flag called `disable_metric_dashboard_refresh_rate` + // See more at: https://gitlab.com/gitlab-org/gitlab/-/issues/229831 + return this.glFeatures.disableMetricDashboardRefreshRate; + }, dropdownText() { return this.refreshInterval?.shortLabel ?? __('Off'); }, @@ -142,7 +150,12 @@ export default { icon="retry" @click="refresh" /> - <gl-new-dropdown v-gl-tooltip :title="s__('Metrics|Set refresh rate')" :text="dropdownText"> + <gl-new-dropdown + v-if="!disableMetricDashboardRefreshRate" + v-gl-tooltip + :title="s__('Metrics|Set refresh rate')" + :text="dropdownText" + > <gl-new-dropdown-item :is-check-item="true" :is-checked="refreshInterval === null" diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue index 792c2f3db34..b4816fa2cb3 100644 --- a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue +++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue @@ -35,7 +35,7 @@ export default { }, }, data() { - return { namespaces: null }; + return { namespaces: null, isForking: false }; }, computed: { @@ -67,6 +67,13 @@ export default { }, }, + methods: { + fork() { + this.isForking = true; + this.$refs.form.submit(); + }, + }, + i18n: { hasReachedProjectLimitMessage: __('You have reached your project limit'), insufficientPermissionsMessage: __( @@ -124,14 +131,17 @@ export default { > <template v-else> <div ref="selectButtonWrapper"> - <form method="POST" :action="group.fork_path"> + <form ref="form" method="POST" :action="group.fork_path"> <input type="hidden" name="authenticity_token" :value="$options.csrf.token" /> <gl-button type="submit" - class="gl-h-7 gl-text-decoration-none!" + class="gl-h-7" :data-qa-name="group.full_name" + category="secondary" variant="success" :disabled="isSelectButtonDisabled" + :loading="isForking" + @click="fork" >{{ __('Select') }}</gl-button > </form> diff --git a/app/assets/javascripts/pages/projects/forks/new/index.js b/app/assets/javascripts/pages/projects/forks/new/index.js index d80e27e9156..79485859738 100644 --- a/app/assets/javascripts/pages/projects/forks/new/index.js +++ b/app/assets/javascripts/pages/projects/forks/new/index.js @@ -1,3 +1,23 @@ -import ProjectFork from '~/project_fork'; +import Vue from 'vue'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import ForkGroupsList from './components/fork_groups_list.vue'; -document.addEventListener('DOMContentLoaded', () => new ProjectFork()); +document.addEventListener('DOMContentLoaded', () => { + const mountElement = document.getElementById('fork-groups-mount-element'); + + const { endpoint, canCreateProject } = mountElement.dataset; + + const hasReachedProjectLimit = !parseBoolean(canCreateProject); + + return new Vue({ + el: mountElement, + render(h) { + return h(ForkGroupsList, { + props: { + endpoint, + hasReachedProjectLimit, + }, + }); + }, + }); +}); diff --git a/app/assets/javascripts/project_fork.js b/app/assets/javascripts/project_fork.js deleted file mode 100644 index f5cd1c3cc3e..00000000000 --- a/app/assets/javascripts/project_fork.js +++ /dev/null @@ -1,9 +0,0 @@ -import $ from 'jquery'; - -export default () => { - $('.js-fork-thumbnail').on('click', function forkThumbnailClicked() { - if ($(this).hasClass('disabled')) return false; - - return $('.js-fork-content').toggleClass('hidden'); - }); -}; diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer.js b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer.js index ed04765c871..3331f4fb20d 100644 --- a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer.js +++ b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer.js @@ -1,7 +1,9 @@ +/* eslint-disable @gitlab/require-i18n-strings */ import { defaults, repeat } from 'lodash'; const DEFAULTS = { subListIndentSpaces: 4, + unorderedListBulletChar: '-', }; const countIndentSpaces = text => { @@ -11,9 +13,12 @@ const countIndentSpaces = text => { }; const buildHTMLToMarkdownRender = (baseRenderer, formattingPreferences = {}) => { - const { subListIndentSpaces } = defaults(formattingPreferences, DEFAULTS); - // eslint-disable-next-line @gitlab/require-i18n-strings + const { subListIndentSpaces, unorderedListBulletChar } = defaults( + formattingPreferences, + DEFAULTS, + ); const sublistNode = 'LI OL, LI UL'; + const unorderedListItemNode = 'UL LI'; return { TEXT_NODE(node) { @@ -47,6 +52,11 @@ const buildHTMLToMarkdownRender = (baseRenderer, formattingPreferences = {}) => return reindentedList; }, + [unorderedListItemNode](node, subContent) { + const baseResult = baseRenderer.convert(node, subContent); + + return baseResult.replace(/^(\s*)([*|-])/, `$1${unorderedListBulletChar}`); + }, }; }; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 438f6c2546e..a1a9489e659 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -472,17 +472,9 @@ margin-right: auto; } - a { - display: block; - width: 100%; - height: 100%; - padding-top: $gl-padding; - text-decoration: none; - - &.disabled { - opacity: 0.3; - cursor: not-allowed; - } + a.disabled { + opacity: 0.3; + cursor: not-allowed; } } @@ -1538,3 +1530,10 @@ pre.light-well { } } } + +@include media-breakpoint-down(xs) { + .fork-filtered-search { + width: 100%; + margin: $gl-spacing-scale-2 0; + } +} diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index f1f41e67a4c..b3fa089a712 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -80,7 +80,7 @@ class Explore::ProjectsController < Explore::ApplicationController # rubocop: disable CodeReuse/ActiveRecord def preload_associations(projects) - projects.includes(:route, :creator, :group, namespace: [:route, :owner]) + projects.includes(:route, :creator, :group, :project_feature, namespace: [:route, :owner]) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index d5da24a76de..99ec100e4c9 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -13,6 +13,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController authorize_metrics_dashboard! push_frontend_feature_flag(:prometheus_computed_alerts) + push_frontend_feature_flag(:disable_metric_dashboard_refresh_rate) end before_action :authorize_read_environment!, except: [:metrics, :additional_metrics, :metrics_dashboard, :metrics_redirect] before_action :authorize_create_environment!, only: [:new, :create] diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index b93f6384e0c..41631aea620 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -36,7 +36,19 @@ class Projects::ForksController < Projects::ApplicationController end def new - @namespaces = fork_service.valid_fork_targets - [project.namespace] + respond_to do |format| + format.html do + @own_namespace = current_user.namespace if fork_service.valid_fork_targets.include?(current_user.namespace) + @project = project + end + + format.json do + namespaces = fork_service.valid_fork_targets - [current_user.namespace, project.namespace] + render json: { + namespaces: ForkNamespaceSerializer.new.represent(namespaces, project: project, current_user: current_user) + } + end + end end # rubocop: disable CodeReuse/ActiveRecord diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index e65e5531b88..5d4514be838 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -84,7 +84,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo @issuable_sidebar = serializer.represent(@merge_request, serializer: 'sidebar') @current_user_data = UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestUserEntity).to_json @show_whitespace_default = current_user.nil? || current_user.show_whitespace_in_diffs - @file_by_file_default = Feature.enabled?(:view_diffs_file_by_file) && current_user&.view_diffs_file_by_file + @file_by_file_default = Feature.enabled?(:view_diffs_file_by_file, default_enabled: true) && current_user&.view_diffs_file_by_file @coverage_path = coverage_reports_project_merge_request_path(@project, @merge_request, format: :json) if @merge_request.has_coverage_reports? @endpoint_metadata_url = endpoint_metadata_url(@project, @merge_request) diff --git a/app/controllers/projects/metrics_dashboard_controller.rb b/app/controllers/projects/metrics_dashboard_controller.rb index 235ee1dfbf2..a7edfea5c09 100644 --- a/app/controllers/projects/metrics_dashboard_controller.rb +++ b/app/controllers/projects/metrics_dashboard_controller.rb @@ -9,6 +9,7 @@ module Projects before_action :authorize_metrics_dashboard! before_action do push_frontend_feature_flag(:prometheus_computed_alerts) + push_frontend_feature_flag(:disable_metric_dashboard_refresh_rate) end def show diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb index d7a6f1b0139..5b12bd000d8 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(:pagerduty_webhook, project) - end - respond_to :json, only: [:reset_alerting_token, :reset_pagerduty_token] helper_method :error_tracking_setting diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index aa118a9bc45..fdea17c2075 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -327,7 +327,8 @@ module ApplicationSettingsHelper :project_download_export_limit, :group_import_limit, :group_export_limit, - :group_download_export_limit + :group_download_export_limit, + :wiki_page_max_content_bytes ] end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 425a0e05c7d..4607f291a26 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -272,6 +272,7 @@ class ApplicationSetting < ApplicationRecord numericality: { greater_than_or_equal_to: 0 } validates :snippet_size_limit, numericality: { only_integer: true, greater_than: 0 } + validates :wiki_page_max_content_bytes, numericality: { only_integer: true, greater_than: 0 } validates :email_restrictions, untrusted_regexp: true diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index c489d11d462..815a570f77a 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -164,7 +164,8 @@ module ApplicationSettingImplementation project_download_export_limit: 1, group_import_limit: 6, group_export_limit: 6, - group_download_export_limit: 1 + group_download_export_limit: 1, + wiki_page_max_content_bytes: 50.megabytes } end diff --git a/app/models/release.rb b/app/models/release.rb index a0245105cd9..4c9d89105d7 100644 --- a/app/models/release.rb +++ b/app/models/release.rb @@ -18,12 +18,10 @@ class Release < ApplicationRecord has_many :milestones, through: :milestone_releases has_many :evidences, inverse_of: :release, class_name: 'Releases::Evidence' - default_value_for :released_at, allows_nil: false do - Time.zone.now - end - accepts_nested_attributes_for :links, allow_destroy: true + before_create :set_released_at + validates :project, :tag, presence: true validates_associated :milestone_releases, message: -> (_, obj) { obj[:value].map(&:errors).map(&:full_messages).join(",") } @@ -90,6 +88,10 @@ class Release < ApplicationRecord repository.find_tag(tag) end end + + def set_released_at + self.released_at ||= created_at + end end Release.prepend_if_ee('EE::Release') diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 3dc90edb331..6a4b4472a03 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -65,6 +65,7 @@ class WikiPage validates :title, presence: true validates :content, presence: true validate :validate_path_limits, if: :title_changed? + validate :validate_content_size_limit, if: :content_changed? # The GitLab Wiki instance. attr_reader :wiki @@ -282,6 +283,10 @@ class WikiPage end end + def content_changed? + attributes[:content] != page&.text_data + end + # Updates the current @attributes hash by merging a hash of params def update_attributes(attrs) attrs[:title] = process_title(attrs[:title]) if attrs[:title].present? @@ -391,4 +396,15 @@ class WikiPage }) end end + + def validate_content_size_limit + current_value = raw_content.to_s.bytesize + max_size = Gitlab::CurrentSettings.wiki_page_max_content_bytes + return if current_value <= max_size + + errors.add(:content, _('is too long (%{current_value}). The maximum size is %{max_size}.') % { + current_value: ActiveSupport::NumberHelper.number_to_human_size(current_value), + max_size: ActiveSupport::NumberHelper.number_to_human_size(max_size) + }) + end end diff --git a/app/services/incident_management/pager_duty/create_incident_issue_service.rb b/app/services/incident_management/pager_duty/create_incident_issue_service.rb index ee0feb49e0d..c0c07f39708 100644 --- a/app/services/incident_management/pager_duty/create_incident_issue_service.rb +++ b/app/services/incident_management/pager_duty/create_incident_issue_service.rb @@ -40,8 +40,7 @@ module IncidentManagement end def webhook_available? - Feature.enabled?(:pagerduty_webhook, project) && - incident_management_setting.pagerduty_active? + incident_management_setting.pagerduty_active? end def forbidden diff --git a/app/services/incident_management/pager_duty/process_webhook_service.rb b/app/services/incident_management/pager_duty/process_webhook_service.rb index 5dd3186694a..fd8252f75fb 100644 --- a/app/services/incident_management/pager_duty/process_webhook_service.rb +++ b/app/services/incident_management/pager_duty/process_webhook_service.rb @@ -39,8 +39,7 @@ module IncidentManagement end def webhook_setting_active? - Feature.enabled?(:pagerduty_webhook, project) && - incident_management_setting.pagerduty_active? + incident_management_setting.pagerduty_active? end def valid_token?(token) diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 6ac53b15ef9..bb660d47887 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -14,10 +14,10 @@ module Projects @valid_fork_targets ||= ForkTargetsFinder.new(@project, current_user).execute end - def valid_fork_target? + def valid_fork_target?(namespace = target_namespace) return true if current_user.admin? - valid_fork_targets.include?(target_namespace) + valid_fork_targets.include?(namespace) end private diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 659b3066603..bc1f2cb3072 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -70,7 +70,7 @@ = f.check_box :show_whitespace_in_diffs, class: 'form-check-input' = f.label :show_whitespace_in_diffs, class: 'form-check-label' do = s_('Preferences|Show whitespace changes in diffs') - - if Feature.enabled?(:view_diffs_file_by_file) + - if Feature.enabled?(:view_diffs_file_by_file, default_enabled: true) .form-group.form-check = f.check_box :view_diffs_file_by_file, class: 'form-check-input' = f.label :view_diffs_file_by_file, class: 'form-check-label' do diff --git a/app/views/projects/forks/_fork_button.html.haml b/app/views/projects/forks/_fork_button.html.haml index eec02a50b85..dd49e8bdb4b 100644 --- a/app/views/projects/forks/_fork_button.html.haml +++ b/app/views/projects/forks/_fork_button.html.haml @@ -1,26 +1,20 @@ - avatar = namespace_icon(namespace, 100) - can_create_project = current_user.can?(:create_projects, namespace) -- if forked_project = namespace.find_fork_of(@project) - .bordered-box.fork-thumbnail.text-center.gl-ml-3.gl-mr-3.gl-mt-3.gl-mb-3.forked - = link_to project_path(forked_project) do - - if /no_((\w*)_)*avatar/.match(avatar) - = group_icon(namespace, class: "avatar rect-avatar s100 identicon mx-auto") - - else - .avatar-container.s100.mx-auto - = image_tag(avatar, class: "avatar s100") - %h5.gl-mt-3 - = namespace.human_name -- else - .bordered-box.fork-thumbnail.text-center.gl-ml-3.gl-mr-3.gl-mt-3.gl-mb-3{ class: ("disabled" unless can_create_project) } - = link_to project_forks_path(@project, namespace_key: namespace.id), - method: "POST", - class: ("disabled has-tooltip" unless can_create_project), - title: (_('You have reached your project limit') unless can_create_project) do - - if /no_((\w*)_)*avatar/.match(avatar) - = group_icon(namespace, class: "avatar rect-avatar s100 identicon mx-auto") - - else - .avatar-container.s100.mx-auto - = image_tag(avatar, class: "avatar s100") - %h5.gl-mt-3{ data: { qa_selector: 'fork_namespace_content', qa_name: namespace.human_name } } - = namespace.human_name +.bordered-box.fork-thumbnail.text-center.gl-m-3{ class: ("disabled" unless can_create_project) } + - if /no_((\w*)_)*avatar/.match(avatar) + = group_icon(namespace, class: "avatar rect-avatar s100 identicon mx-auto") + - else + .avatar-container.s100.mx-auto.gl-mt-5 + = image_tag(avatar, class: "avatar s100") + %h5.gl-mt-3 + = namespace.human_name + - if forked_project = namespace.find_fork_of(@project) + = link_to _("Go to project"), project_path(forked_project), class: "btn" + - else + %div{ class: ('has-tooltip' unless can_create_project), + title: (_('You have reached your project limit') unless can_create_project) } + = link_to _("Select"), project_forks_path(@project, namespace_key: namespace.id), + data: { qa_selector: 'fork_namespace_button', qa_name: namespace.human_name }, + method: "POST", + class: ["btn btn-success", ("disabled" unless can_create_project)] diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml index 887081d0f35..daac118d88e 100644 --- a/app/views/projects/forks/new.html.haml +++ b/app/views/projects/forks/new.html.haml @@ -7,15 +7,10 @@ %p = _("A fork is a copy of a project.<br />Forking a repository allows you to make changes without affecting the original project.").html_safe .col-lg-9 - - if @namespaces.present? + - if @own_namespace.present? .fork-thumbnail-container.js-fork-content %h5.gl-mt-0.gl-mb-0.gl-ml-3.gl-mr-3 = _("Select a namespace to fork the project") - - @namespaces.each do |namespace| - = render 'fork_button', namespace: namespace - - else - %strong - = _("No available namespaces to fork the project.") - %p.gl-mt-3 - = _("You must have permission to create a project in a namespace before forking.") + = render 'fork_button', namespace: @own_namespace + #fork-groups-mount-element{ data: { endpoint: new_project_fork_path(@project, format: :json), can_create_project: current_user.can_create_project?.to_s } } |