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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-06-22 18:09:27 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-06-22 18:09:27 +0300
commite829ca213b25b49faa619c0363059c6ed0a5a112 (patch)
tree0db00d8f17cf6f278cc68fb6b3739fda8d44fea6 /app
parent6046a605fdbb6d180861c978d17fe3516b2e7507 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/badges/components/badge_form.vue2
-rw-r--r--app/assets/javascripts/batch_comments/components/preview_dropdown.vue2
-rw-r--r--app/assets/javascripts/blob/notebook/notebook_viewer.vue4
-rw-r--r--app/assets/javascripts/blob/pdf/pdf_viewer.vue2
-rw-r--r--app/assets/javascripts/blob/sketch/index.js2
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue2
-rw-r--r--app/assets/javascripts/diffs/components/app.vue2
-rw-r--r--app/assets/javascripts/issuable_suggestions/components/app.vue2
-rw-r--r--app/assets/javascripts/issue_show/components/edit_actions.vue2
-rw-r--r--app/assets/javascripts/jobs/components/environments_block.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/header_component.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue2
-rw-r--r--app/assets/javascripts/serverless/components/function_details.vue2
-rw-r--r--app/assets/javascripts/serverless/components/functions.vue8
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue1
-rw-r--r--app/assets/stylesheets/framework/common.scss1
-rw-r--r--app/controllers/concerns/known_sign_in.rb2
-rw-r--r--app/controllers/dashboard/todos_controller.rb3
-rw-r--r--app/controllers/instance_statistics/cohorts_controller.rb4
-rw-r--r--app/controllers/instance_statistics/dev_ops_score_controller.rb4
-rw-r--r--app/controllers/projects/cycle_analytics_controller.rb3
-rw-r--r--app/controllers/projects/graphs_controller.rb3
-rw-r--r--app/controllers/projects/pipelines_controller.rb3
-rw-r--r--app/finders/ci/pipelines_finder.rb2
-rw-r--r--app/finders/ci/runner_jobs_finder.rb2
-rw-r--r--app/helpers/analytics/unique_visits_helper.rb32
-rw-r--r--app/helpers/application_settings_helper.rb1
-rw-r--r--app/helpers/markup_helper.rb1
-rw-r--r--app/models/application_setting_implementation.rb1
-rw-r--r--app/models/audit_event.rb1
-rw-r--r--app/models/ci/pipeline.rb4
-rw-r--r--app/models/ci/stage.rb6
-rw-r--r--app/models/commit_status.rb2
-rw-r--r--app/models/concerns/ci/has_status.rb168
-rw-r--r--app/models/concerns/has_status.rb166
-rw-r--r--app/models/environment.rb2
-rw-r--r--app/models/project.rb5
-rw-r--r--app/models/user.rb9
-rw-r--r--app/policies/base_policy.rb4
-rw-r--r--app/policies/concerns/policy_actor.rb4
-rw-r--r--app/policies/project_policy.rb10
-rw-r--r--app/serializers/stage_entity.rb4
-rw-r--r--app/services/authorized_project_update/periodic_recalculate_service.rb18
-rw-r--r--app/services/authorized_project_update/recalculate_for_user_range_service.rb20
-rw-r--r--app/services/ci/pipeline_processing/atomic_processing_service.rb2
-rw-r--r--app/services/ci/pipeline_processing/atomic_processing_service/status_collection.rb2
-rw-r--r--app/services/ci/pipeline_processing/legacy_processing_service.rb4
-rw-r--r--app/services/projects/update_repository_storage_service.rb14
-rw-r--r--app/services/users/refresh_authorized_projects_service.rb2
-rw-r--r--app/views/admin/appearances/_form.html.haml2
-rw-r--r--app/views/admin/application_settings/_signin.html.haml9
-rw-r--r--app/views/admin/groups/index.html.haml2
-rw-r--r--app/views/admin/hook_logs/_index.html.haml2
-rw-r--r--app/views/admin/hooks/edit.html.haml2
-rw-r--r--app/views/admin/hooks/index.html.haml2
-rw-r--r--app/views/admin/users/_head.html.haml2
-rw-r--r--app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml2
-rw-r--r--app/views/doorkeeper/applications/index.html.haml2
-rw-r--r--app/views/groups/settings/_lfs.html.haml2
-rw-r--r--app/views/groups/settings/_permissions.html.haml6
-rw-r--r--app/views/profiles/accounts/show.html.haml2
-rw-r--r--app/views/profiles/active_sessions/index.html.haml2
-rw-r--r--app/views/profiles/emails/index.html.haml2
-rw-r--r--app/views/profiles/gpg_keys/index.html.haml2
-rw-r--r--app/views/profiles/keys/index.html.haml2
-rw-r--r--app/views/profiles/notifications/show.html.haml2
-rw-r--r--app/views/profiles/passwords/edit.html.haml2
-rw-r--r--app/views/profiles/show.html.haml4
-rw-r--r--app/views/projects/_files.html.haml2
-rw-r--r--app/views/projects/_wiki.html.haml2
-rw-r--r--app/views/projects/blob/_editor.html.haml2
-rw-r--r--app/views/projects/blob/viewers/_loading.html.haml2
-rw-r--r--app/views/projects/blob/viewers/_sketch.html.haml2
-rw-r--r--app/views/projects/blob/viewers/_stl.html.haml4
-rw-r--r--app/views/projects/environments/_form.html.haml2
-rw-r--r--app/views/projects/forks/_fork_button.html.haml4
-rw-r--r--app/views/projects/hook_logs/_index.html.haml2
-rw-r--r--app/views/projects/hook_logs/show.html.haml2
-rw-r--r--app/views/projects/hooks/edit.html.haml2
-rw-r--r--app/views/projects/hooks/index.html.haml2
-rw-r--r--app/views/projects/issues/_by_email_description.html.haml4
-rw-r--r--app/views/projects/mirrors/_instructions.html.haml2
-rw-r--r--app/views/projects/pipelines/_stage.html.haml2
-rw-r--r--app/views/projects/protected_branches/show.html.haml2
-rw-r--r--app/views/projects/protected_tags/show.html.haml2
-rw-r--r--app/views/projects/services/_form.html.haml2
-rw-r--r--app/views/projects/services/prometheus/_help.html.haml2
-rw-r--r--app/views/projects/services/prometheus/_show.html.haml2
-rw-r--r--app/views/projects/settings/operations/_prometheus.html.haml2
-rw-r--r--app/views/projects/tags/show.html.haml2
-rw-r--r--app/views/projects/triggers/_index.html.haml4
-rw-r--r--app/views/projects/triggers/edit.html.haml2
-rw-r--r--app/views/shared/_commit_well.html.haml2
-rw-r--r--app/views/shared/_md_preview.html.haml4
-rw-r--r--app/views/shared/file_hooks/_index.html.haml2
-rw-r--r--app/views/shared/issuable/form/_default_templates.html.haml2
-rw-r--r--app/views/shared/issuable/form/_merge_params.html.haml2
-rw-r--r--app/views/shared/issuable/form/_title.html.haml4
-rw-r--r--app/views/shared/notes/_hints.html.haml6
-rw-r--r--app/views/shared/web_hooks/_index.html.haml2
-rw-r--r--app/views/shared/wikis/_sidebar.html.haml2
-rw-r--r--app/views/shared/wikis/show.html.haml2
-rw-r--r--app/views/users/_overview.html.haml2
-rw-r--r--app/workers/all_queues.yml18
-rw-r--r--app/workers/authorized_project_update/periodic_recalculate_worker.rb20
-rw-r--r--app/workers/authorized_project_update/user_refresh_over_user_range_worker.rb20
112 files changed, 467 insertions, 287 deletions
diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue
index dccc0b024ba..9e889ff87f2 100644
--- a/app/assets/javascripts/badges/components/badge_form.vue
+++ b/app/assets/javascripts/badges/components/badge_form.vue
@@ -164,7 +164,7 @@ export default {
<template>
<form
:class="{ 'was-validated': wasValidated }"
- class="prepend-top-default append-bottom-default needs-validation"
+ class="prepend-top-default gl-mb-3 needs-validation"
novalidate
@submit.prevent.stop="onSubmit"
>
diff --git a/app/assets/javascripts/batch_comments/components/preview_dropdown.vue b/app/assets/javascripts/batch_comments/components/preview_dropdown.vue
index 195e1b7ec5c..be211c9ef87 100644
--- a/app/assets/javascripts/batch_comments/components/preview_dropdown.vue
+++ b/app/assets/javascripts/batch_comments/components/preview_dropdown.vue
@@ -96,7 +96,7 @@ export default {
<preview-item :draft="draft" :is-last="isLast(index)" />
</li>
</ul>
- <gl-loading-icon v-else size="lg" class="prepend-top-default append-bottom-default" />
+ <gl-loading-icon v-else size="lg" class="prepend-top-default gl-mb-3" />
</div>
<div class="dropdown-footer">
<publish-button
diff --git a/app/assets/javascripts/blob/notebook/notebook_viewer.vue b/app/assets/javascripts/blob/notebook/notebook_viewer.vue
index 401fe9beb62..8ee012de2d7 100644
--- a/app/assets/javascripts/blob/notebook/notebook_viewer.vue
+++ b/app/assets/javascripts/blob/notebook/notebook_viewer.vue
@@ -62,9 +62,7 @@ export default {
</script>
<template>
- <div
- class="js-notebook-viewer-mounted container-fluid md prepend-top-default append-bottom-default"
- >
+ <div class="js-notebook-viewer-mounted container-fluid md prepend-top-default gl-mb-3">
<div v-if="loading && !error" class="text-center loading">
<gl-loading-icon class="mt-5" size="lg" />
</div>
diff --git a/app/assets/javascripts/blob/pdf/pdf_viewer.vue b/app/assets/javascripts/blob/pdf/pdf_viewer.vue
index 5eaddfc099a..ed9a655fccb 100644
--- a/app/assets/javascripts/blob/pdf/pdf_viewer.vue
+++ b/app/assets/javascripts/blob/pdf/pdf_viewer.vue
@@ -34,7 +34,7 @@ export default {
</script>
<template>
- <div class="js-pdf-viewer container-fluid md prepend-top-default append-bottom-default">
+ <div class="js-pdf-viewer container-fluid md prepend-top-default gl-mb-3">
<div v-if="loading && !error" class="text-center loading">
<gl-loading-icon class="mt-5" size="lg" />
</div>
diff --git a/app/assets/javascripts/blob/sketch/index.js b/app/assets/javascripts/blob/sketch/index.js
index dbff03dc734..922f701fd59 100644
--- a/app/assets/javascripts/blob/sketch/index.js
+++ b/app/assets/javascripts/blob/sketch/index.js
@@ -56,7 +56,7 @@ export default class SketchLoader {
error() {
const errorMsg = document.createElement('p');
- errorMsg.className = 'prepend-top-default append-bottom-default text-center';
+ errorMsg.className = 'prepend-top-default gl-mb-3 text-center';
errorMsg.textContent = __(`
Cannot show preview. For previews on sketch files, they must have the file format
introduced by Sketch version 43 and above.
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index df31ee65b27..c747b63473a 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -115,7 +115,7 @@ export default {
</script>
<template>
- <div class="append-bottom-default deploy-keys">
+ <div class="gl-mb-3 deploy-keys">
<gl-loading-icon
v-if="isLoading && !hasKeys"
:label="s__('DeployKeys|Loading deploy keys')"
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 941365d9d1d..406b0098d99 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -398,7 +398,7 @@ export default {
class="files d-flex"
>
<div
- v-show="showTreeList"
+ v-if="showTreeList"
:style="{ width: `${treeWidth}px` }"
class="diff-tree-list js-diff-tree-list mr-3"
>
diff --git a/app/assets/javascripts/issuable_suggestions/components/app.vue b/app/assets/javascripts/issuable_suggestions/components/app.vue
index 67d10b797fb..810ca7ac1bd 100644
--- a/app/assets/javascripts/issuable_suggestions/components/app.vue
+++ b/app/assets/javascripts/issuable_suggestions/components/app.vue
@@ -84,7 +84,7 @@ export default {
v-for="(suggestion, index) in issues"
:key="suggestion.id"
:class="{
- 'append-bottom-default': index !== issues.length - 1,
+ 'gl-mb-3': index !== issues.length - 1,
}"
>
<suggestion :suggestion="suggestion" />
diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue
index 588ae655de4..f6e9ff34b8f 100644
--- a/app/assets/javascripts/issue_show/components/edit_actions.vue
+++ b/app/assets/javascripts/issue_show/components/edit_actions.vue
@@ -63,7 +63,7 @@ export default {
</script>
<template>
- <div class="prepend-top-default append-bottom-default clearfix">
+ <div class="prepend-top-default gl-mb-3 clearfix">
<button
:class="{ disabled: formState.updateLoading || !isSubmitEnabled }"
:disabled="formState.updateLoading || !isSubmitEnabled"
diff --git a/app/assets/javascripts/jobs/components/environments_block.vue b/app/assets/javascripts/jobs/components/environments_block.vue
index c34a3488dbd..010f588e58b 100644
--- a/app/assets/javascripts/jobs/components/environments_block.vue
+++ b/app/assets/javascripts/jobs/components/environments_block.vue
@@ -274,7 +274,7 @@ export default {
};
</script>
<template>
- <div class="prepend-top-default append-bottom-default js-environment-container">
+ <div class="prepend-top-default gl-mb-3 js-environment-container">
<div class="environment-information">
<ci-icon :status="iconStatus" />
<p class="inline gl-mb-0" v-html="environment"></p>
diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue
index e7777d0d3af..9c9d9a46ae1 100644
--- a/app/assets/javascripts/pipelines/components/header_component.vue
+++ b/app/assets/javascripts/pipelines/components/header_component.vue
@@ -108,7 +108,7 @@ export default {
/>
</ci-header>
- <gl-loading-icon v-if="isLoading" size="lg" class="prepend-top-default append-bottom-default" />
+ <gl-loading-icon v-if="isLoading" size="lg" class="prepend-top-default gl-mb-3" />
<gl-modal
:modal-id="$options.DELETE_MODAL_ID"
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
index fe5c289152d..46fe16e5580 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_suite_table.vue
@@ -42,7 +42,7 @@ export default {
</div>
</div>
- <div v-if="hasSuites" class="test-reports-table append-bottom-default js-test-cases-table">
+ <div v-if="hasSuites" class="test-reports-table gl-mb-3 js-test-cases-table">
<div role="row" class="gl-responsive-table-row table-row-header font-weight-bold fgray">
<div role="rowheader" class="table-section section-20">
{{ __('Class') }}
diff --git a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
index 4dfb67dd8e8..6d5d7b6450b 100644
--- a/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
+++ b/app/assets/javascripts/pipelines/components/test_reports/test_summary_table.vue
@@ -46,7 +46,7 @@ export default {
</div>
</div>
- <div v-if="hasSuites" class="test-reports-table append-bottom-default js-test-suites-table">
+ <div v-if="hasSuites" class="test-reports-table gl-mb-3 js-test-suites-table">
<div role="row" class="gl-responsive-table-row table-row-header font-weight-bold">
<div role="rowheader" class="table-section section-25 pl-3">
{{ __('Suite') }}
diff --git a/app/assets/javascripts/serverless/components/function_details.vue b/app/assets/javascripts/serverless/components/function_details.vue
index 2ac57ac5bcb..53c78b93254 100644
--- a/app/assets/javascripts/serverless/components/function_details.vue
+++ b/app/assets/javascripts/serverless/components/function_details.vue
@@ -71,7 +71,7 @@ export default {
<template>
<section id="serverless-function-details">
<h3 class="serverless-function-name">{{ name }}</h3>
- <div class="append-bottom-default serverless-function-description">
+ <div class="gl-mb-3 serverless-function-description">
<div v-for="(line, index) in description.split('\n')" :key="index">{{ line }}</div>
</div>
<url :uri="funcUrl" />
diff --git a/app/assets/javascripts/serverless/components/functions.vue b/app/assets/javascripts/serverless/components/functions.vue
index 2b1291ac70f..0995c1a6b5e 100644
--- a/app/assets/javascripts/serverless/components/functions.vue
+++ b/app/assets/javascripts/serverless/components/functions.vue
@@ -75,11 +75,7 @@ export default {
<template>
<section id="serverless-functions" class="flex-grow">
- <gl-loading-icon
- v-if="checkingInstalled"
- size="lg"
- class="prepend-top-default append-bottom-default"
- />
+ <gl-loading-icon v-if="checkingInstalled" size="lg" class="prepend-top-default gl-mb-3" />
<div v-else-if="isInstalled">
<div v-if="hasFunctionData">
@@ -98,7 +94,7 @@ export default {
<gl-loading-icon
v-if="isLoading"
size="lg"
- class="prepend-top-default append-bottom-default js-functions-loader"
+ class="prepend-top-default gl-mb-3 js-functions-loader"
/>
</div>
<div v-else class="empty-state js-empty-state">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
index 0464c4b9c15..d147c32b58b 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
@@ -77,7 +77,7 @@ export default {
};
</script>
<template>
- <div class="d-flex mr-source-target append-bottom-default">
+ <div class="d-flex mr-source-target gl-mb-3">
<mr-widget-icon name="git-merge" />
<div class="git-merge-container d-flex">
<div class="normal">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue
index 9942861d9e4..36413198a06 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue
@@ -34,7 +34,7 @@ export default {
};
</script>
<template>
- <div :id="$options.popoverContainer" class="d-flex mr-pipeline-suggest append-bottom-default">
+ <div :id="$options.popoverContainer" class="d-flex mr-pipeline-suggest gl-mb-3">
<mr-widget-icon :name="$options.iconName" />
<div :id="$options.popoverTarget">
<gl-sprintf
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue
index acd8037cfb2..44bdc4a3be8 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/commit_edit.vue
@@ -29,7 +29,7 @@ export default {
<textarea
:id="inputId"
:value="value"
- class="form-control js-gfm-input append-bottom-default commit-message-edit"
+ class="form-control js-gfm-input gl-mb-3 commit-message-edit"
dir="auto"
required="required"
rows="7"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 89844f07e7e..8dcfe9b4512 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -231,7 +231,7 @@ export default {
<template>
<div
ref="gl-form"
- :class="{ 'prepend-top-default append-bottom-default': addSpacingClasses }"
+ :class="{ 'prepend-top-default gl-mb-3': addSpacingClasses }"
class="js-vue-markdown-field md-area position-relative"
>
<markdown-header
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index aa1abb5adb6..049f5e71849 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -89,14 +89,13 @@ export default {
<div class="md-header">
<ul class="nav-links clearfix">
<li :class="{ active: !previewMarkdown }" class="md-header-tab">
- <button class="js-write-link" tabindex="-1" type="button" @click="writeMarkdownTab($event)">
+ <button class="js-write-link" type="button" @click="writeMarkdownTab($event)">
{{ __('Write') }}
</button>
</li>
<li :class="{ active: previewMarkdown }" class="md-header-tab">
<button
class="js-preview-link js-md-preview-button"
- tabindex="-1"
type="button"
@click="previewMarkdownTab($event)"
>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
index 94f78c0c085..f37dd9e171c 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
@@ -64,7 +64,6 @@ export default {
:aria-label="buttonTitle"
type="button"
class="toolbar-btn js-md"
- tabindex="-1"
data-container="body"
@click="() => $emit('click')"
>
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 1d756210b9c..5166c4c640d 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -421,7 +421,6 @@ img.emoji {
.append-bottom-10 { margin-bottom: 10px; }
.append-bottom-15 { margin-bottom: 15px; }
.append-bottom-20 { margin-bottom: 20px; }
-.append-bottom-default { margin-bottom: $gl-padding; }
.prepend-bottom-32 { margin-bottom: 32px; }
.ml-10 { margin-left: 4.5rem; }
.inline { display: inline-block; }
diff --git a/app/controllers/concerns/known_sign_in.rb b/app/controllers/concerns/known_sign_in.rb
index 2b73042a91b..cacc7e4628f 100644
--- a/app/controllers/concerns/known_sign_in.rb
+++ b/app/controllers/concerns/known_sign_in.rb
@@ -10,7 +10,7 @@ module KnownSignIn
private
def verify_known_sign_in
- return unless current_user
+ return unless Gitlab::CurrentSettings.notify_on_unknown_sign_in? && current_user
notify_user unless known_device? || known_remote_ip?
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 8a8064b24c2..db40b0bed77 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -3,11 +3,14 @@
class Dashboard::TodosController < Dashboard::ApplicationController
include ActionView::Helpers::NumberHelper
include PaginatedCollection
+ include Analytics::UniqueVisitsHelper
before_action :authorize_read_project!, only: :index
before_action :authorize_read_group!, only: :index
before_action :find_todos, only: [:index, :destroy_all]
+ track_unique_visits :index, target_id: 'u_analytics_todos'
+
def index
@sort = params[:sort]
@todos = @todos.page(params[:page])
diff --git a/app/controllers/instance_statistics/cohorts_controller.rb b/app/controllers/instance_statistics/cohorts_controller.rb
index 4b4e39db2e1..0de62a56b01 100644
--- a/app/controllers/instance_statistics/cohorts_controller.rb
+++ b/app/controllers/instance_statistics/cohorts_controller.rb
@@ -1,8 +1,12 @@
# frozen_string_literal: true
class InstanceStatistics::CohortsController < InstanceStatistics::ApplicationController
+ include Analytics::UniqueVisitsHelper
+
before_action :authenticate_usage_ping_enabled_or_admin!
+ track_unique_visits :index, target_id: 'i_analytics_cohorts'
+
def index
if Gitlab::CurrentSettings.usage_ping_enabled
cohorts_results = Rails.cache.fetch('cohorts', expires_in: 1.day) do
diff --git a/app/controllers/instance_statistics/dev_ops_score_controller.rb b/app/controllers/instance_statistics/dev_ops_score_controller.rb
index 238f7fa7707..b98a1bf7f99 100644
--- a/app/controllers/instance_statistics/dev_ops_score_controller.rb
+++ b/app/controllers/instance_statistics/dev_ops_score_controller.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
class InstanceStatistics::DevOpsScoreController < InstanceStatistics::ApplicationController
+ include Analytics::UniqueVisitsHelper
+
+ track_unique_visits :index, target_id: 'i_analytics_dev_ops_score'
+
# rubocop: disable CodeReuse/ActiveRecord
def index
@metric = DevOpsScore::Metric.order(:created_at).last&.present
diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb
index f13c75ac4cc..898d888c978 100644
--- a/app/controllers/projects/cycle_analytics_controller.rb
+++ b/app/controllers/projects/cycle_analytics_controller.rb
@@ -4,10 +4,13 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
include ActionView::Helpers::DateHelper
include ActionView::Helpers::TextHelper
include CycleAnalyticsParams
+ include Analytics::UniqueVisitsHelper
before_action :whitelist_query_limiting, only: [:show]
before_action :authorize_read_cycle_analytics!
+ track_unique_visits :show, target_id: 'p_analytics_valuestream'
+
def show
@cycle_analytics = ::CycleAnalytics::ProjectLevel.new(@project, options: options(cycle_analytics_project_params))
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index a8b90f8685f..9b889f9e837 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -2,12 +2,15 @@
class Projects::GraphsController < Projects::ApplicationController
include ExtractsPath
+ include Analytics::UniqueVisitsHelper
# Authorize
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :authorize_read_repository_graphs!
+ track_unique_visits :charts, target_id: 'p_analytics_repo'
+
def show
respond_to do |format|
format.html
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index a8189a82c56..f27ac8b53e4 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -2,6 +2,7 @@
class Projects::PipelinesController < Projects::ApplicationController
include ::Gitlab::Utils::StrongMemoize
+ include Analytics::UniqueVisitsHelper
before_action :whitelist_query_limiting, only: [:create, :retry]
before_action :pipeline, except: [:index, :new, :create, :charts]
@@ -20,6 +21,8 @@ class Projects::PipelinesController < Projects::ApplicationController
around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
+ track_unique_visits :charts, target_id: 'p_analytics_pipelines'
+
wrap_parameters Ci::Pipeline
POLLING_INTERVAL = 10_000
diff --git a/app/finders/ci/pipelines_finder.rb b/app/finders/ci/pipelines_finder.rb
index 9e71e92b456..7347a83d294 100644
--- a/app/finders/ci/pipelines_finder.rb
+++ b/app/finders/ci/pipelines_finder.rb
@@ -71,7 +71,7 @@ module Ci
# rubocop: disable CodeReuse/ActiveRecord
def by_status(items)
- return items unless HasStatus::AVAILABLE_STATUSES.include?(params[:status])
+ return items unless Ci::HasStatus::AVAILABLE_STATUSES.include?(params[:status])
items.where(status: params[:status])
end
diff --git a/app/finders/ci/runner_jobs_finder.rb b/app/finders/ci/runner_jobs_finder.rb
index ffcdb407e7e..9dc3c2a2427 100644
--- a/app/finders/ci/runner_jobs_finder.rb
+++ b/app/finders/ci/runner_jobs_finder.rb
@@ -21,7 +21,7 @@ module Ci
# rubocop: disable CodeReuse/ActiveRecord
def by_status(items)
- return items unless HasStatus::AVAILABLE_STATUSES.include?(params[:status])
+ return items unless Ci::HasStatus::AVAILABLE_STATUSES.include?(params[:status])
items.where(status: params[:status])
end
diff --git a/app/helpers/analytics/unique_visits_helper.rb b/app/helpers/analytics/unique_visits_helper.rb
new file mode 100644
index 00000000000..ded7f54e44e
--- /dev/null
+++ b/app/helpers/analytics/unique_visits_helper.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Analytics
+ module UniqueVisitsHelper
+ extend ActiveSupport::Concern
+
+ 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
+
+ def track_visit(target_id)
+ return unless Feature.enabled?(:track_unique_visits)
+ return unless Gitlab::CurrentSettings.usage_ping_enabled?
+ return unless visitor_id
+
+ Gitlab::Analytics::UniqueVisits.new.track_visit(visitor_id, target_id)
+ end
+
+ class_methods do
+ def track_unique_visits(controller_actions, target_id:)
+ after_action only: controller_actions, if: -> { request.format.html? && request.headers['DNT'] != '1' } do
+ track_visit(target_id)
+ end
+ end
+ end
+ end
+end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index e709d15a946..cc652484be1 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -244,6 +244,7 @@ module ApplicationSettingsHelper
:metrics_method_call_threshold,
:minimum_password_length,
:mirror_available,
+ :notify_on_unknown_sign_in,
:pages_domain_verification_enabled,
:password_authentication_enabled_for_web,
:password_authentication_enabled_for_git,
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index 7ab2b33de8c..ed8931fe0f2 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -244,7 +244,6 @@ module MarkupHelper
content_tag :button,
type: 'button',
class: 'toolbar-btn js-md has-tooltip',
- tabindex: -1,
data: data,
title: options[:title],
aria: { label: options[:title] } do
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
index d24136cc04a..05661f9643b 100644
--- a/app/models/application_setting_implementation.rb
+++ b/app/models/application_setting_implementation.rb
@@ -88,6 +88,7 @@ module ApplicationSettingImplementation
max_attachment_size: Settings.gitlab['max_attachment_size'],
max_import_size: 50,
mirror_available: true,
+ notify_on_unknown_sign_in: true,
outbound_local_requests_whitelist: [],
password_authentication_enabled_for_git: true,
password_authentication_enabled_for_web: Settings.gitlab['signin_enabled'],
diff --git a/app/models/audit_event.rb b/app/models/audit_event.rb
index 3bbd2e43a51..22f4dcf70b6 100644
--- a/app/models/audit_event.rb
+++ b/app/models/audit_event.rb
@@ -16,6 +16,7 @@ class AuditEvent < ApplicationRecord
scope :by_entity_type, -> (entity_type) { where(entity_type: entity_type) }
scope :by_entity_id, -> (entity_id) { where(entity_id: entity_id) }
+ scope :by_author_id, -> (author_id) { where(author_id: author_id) }
after_initialize :initialize_details
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 8e8fd774310..669c80d5433 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -3,7 +3,7 @@
module Ci
class Pipeline < ApplicationRecord
extend Gitlab::Ci::Model
- include HasStatus
+ include Ci::HasStatus
include Importable
include AfterCommitQueue
include Presentable
@@ -640,7 +640,7 @@ module Ci
when 'manual' then block
when 'scheduled' then delay
else
- raise HasStatus::UnknownStatusError,
+ raise Ci::HasStatus::UnknownStatusError,
"Unknown status `#{new_status}`"
end
end
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index a316b4718e0..41215601704 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -4,10 +4,10 @@ module Ci
class Stage < ApplicationRecord
extend Gitlab::Ci::Model
include Importable
- include HasStatus
+ include Ci::HasStatus
include Gitlab::OptimisticLocking
- enum status: HasStatus::STATUSES_ENUM
+ enum status: Ci::HasStatus::STATUSES_ENUM
belongs_to :project
belongs_to :pipeline
@@ -98,7 +98,7 @@ module Ci
when 'scheduled' then delay
when 'skipped', nil then skip
else
- raise HasStatus::UnknownStatusError,
+ raise Ci::HasStatus::UnknownStatusError,
"Unknown status `#{new_status}`"
end
end
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 475f82f23ca..cb22a9268fb 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class CommitStatus < ApplicationRecord
- include HasStatus
+ include Ci::HasStatus
include Importable
include AfterCommitQueue
include Presentable
diff --git a/app/models/concerns/ci/has_status.rb b/app/models/concerns/ci/has_status.rb
new file mode 100644
index 00000000000..c52807ec501
--- /dev/null
+++ b/app/models/concerns/ci/has_status.rb
@@ -0,0 +1,168 @@
+# frozen_string_literal: true
+
+module Ci
+ module HasStatus
+ extend ActiveSupport::Concern
+
+ DEFAULT_STATUS = 'created'
+ BLOCKED_STATUS = %w[manual scheduled].freeze
+ AVAILABLE_STATUSES = %w[created waiting_for_resource preparing pending running success failed canceled skipped manual scheduled].freeze
+ STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
+ ACTIVE_STATUSES = %w[waiting_for_resource preparing pending running].freeze
+ COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
+ ORDERED_STATUSES = %w[failed preparing pending running waiting_for_resource manual scheduled canceled success skipped created].freeze
+ PASSED_WITH_WARNINGS_STATUSES = %w[failed canceled].to_set.freeze
+ EXCLUDE_IGNORED_STATUSES = %w[manual failed canceled].to_set.freeze
+ STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
+ failed: 4, canceled: 5, skipped: 6, manual: 7,
+ scheduled: 8, preparing: 9, waiting_for_resource: 10 }.freeze
+
+ UnknownStatusError = Class.new(StandardError)
+
+ class_methods do
+ def legacy_status_sql
+ scope_relevant = respond_to?(:exclude_ignored) ? exclude_ignored : all
+ scope_warnings = respond_to?(:failed_but_allowed) ? failed_but_allowed : none
+
+ builds = scope_relevant.select('count(*)').to_sql
+ created = scope_relevant.created.select('count(*)').to_sql
+ success = scope_relevant.success.select('count(*)').to_sql
+ manual = scope_relevant.manual.select('count(*)').to_sql
+ scheduled = scope_relevant.scheduled.select('count(*)').to_sql
+ preparing = scope_relevant.preparing.select('count(*)').to_sql
+ waiting_for_resource = scope_relevant.waiting_for_resource.select('count(*)').to_sql
+ pending = scope_relevant.pending.select('count(*)').to_sql
+ running = scope_relevant.running.select('count(*)').to_sql
+ skipped = scope_relevant.skipped.select('count(*)').to_sql
+ canceled = scope_relevant.canceled.select('count(*)').to_sql
+ warnings = scope_warnings.select('count(*) > 0').to_sql.presence || 'false'
+
+ Arel.sql(
+ "(CASE
+ WHEN (#{builds})=(#{skipped}) AND (#{warnings}) THEN 'success'
+ WHEN (#{builds})=(#{skipped}) THEN 'skipped'
+ WHEN (#{builds})=(#{success}) THEN 'success'
+ WHEN (#{builds})=(#{created}) THEN 'created'
+ WHEN (#{builds})=(#{preparing}) THEN 'preparing'
+ WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success'
+ WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled'
+ WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
+ WHEN (#{running})+(#{pending})>0 THEN 'running'
+ WHEN (#{waiting_for_resource})>0 THEN 'waiting_for_resource'
+ WHEN (#{manual})>0 THEN 'manual'
+ WHEN (#{scheduled})>0 THEN 'scheduled'
+ WHEN (#{preparing})>0 THEN 'preparing'
+ WHEN (#{created})>0 THEN 'running'
+ ELSE 'failed'
+ END)"
+ )
+ end
+
+ def legacy_status
+ all.pluck(legacy_status_sql).first
+ end
+
+ # This method should not be used.
+ # This method performs expensive calculation of status:
+ # 1. By plucking all related objects,
+ # 2. Or executes expensive SQL query
+ def slow_composite_status(project:)
+ if ::Gitlab::Ci::Features.composite_status?(project)
+ Gitlab::Ci::Status::Composite
+ .new(all, with_allow_failure: columns_hash.key?('allow_failure'))
+ .status
+ else
+ legacy_status
+ end
+ end
+
+ def started_at
+ all.minimum(:started_at)
+ end
+
+ def finished_at
+ all.maximum(:finished_at)
+ end
+
+ def all_state_names
+ state_machines.values.flat_map(&:states).flat_map { |s| s.map(&:name) }
+ end
+
+ def completed_statuses
+ COMPLETED_STATUSES.map(&:to_sym)
+ end
+ end
+
+ included do
+ validates :status, inclusion: { in: AVAILABLE_STATUSES }
+
+ state_machine :status, initial: :created do
+ state :created, value: 'created'
+ state :waiting_for_resource, value: 'waiting_for_resource'
+ state :preparing, value: 'preparing'
+ state :pending, value: 'pending'
+ state :running, value: 'running'
+ state :failed, value: 'failed'
+ state :success, value: 'success'
+ state :canceled, value: 'canceled'
+ state :skipped, value: 'skipped'
+ state :manual, value: 'manual'
+ state :scheduled, value: 'scheduled'
+ end
+
+ scope :created, -> { with_status(:created) }
+ scope :waiting_for_resource, -> { with_status(:waiting_for_resource) }
+ scope :preparing, -> { with_status(:preparing) }
+ scope :relevant, -> { without_status(:created) }
+ scope :running, -> { with_status(:running) }
+ scope :pending, -> { with_status(:pending) }
+ scope :success, -> { with_status(:success) }
+ scope :failed, -> { with_status(:failed) }
+ scope :canceled, -> { with_status(:canceled) }
+ scope :skipped, -> { with_status(:skipped) }
+ scope :manual, -> { with_status(:manual) }
+ scope :scheduled, -> { with_status(:scheduled) }
+ scope :alive, -> { with_status(:created, :waiting_for_resource, :preparing, :pending, :running) }
+ scope :alive_or_scheduled, -> { with_status(:created, :waiting_for_resource, :preparing, :pending, :running, :scheduled) }
+ scope :created_or_pending, -> { with_status(:created, :pending) }
+ scope :running_or_pending, -> { with_status(:running, :pending) }
+ scope :finished, -> { with_status(:success, :failed, :canceled) }
+ scope :failed_or_canceled, -> { with_status(:failed, :canceled) }
+ scope :incomplete, -> { without_statuses(completed_statuses) }
+
+ scope :cancelable, -> do
+ where(status: [:running, :waiting_for_resource, :preparing, :pending, :created, :scheduled])
+ end
+
+ scope :without_statuses, -> (names) do
+ with_status(all_state_names - names.to_a)
+ end
+ end
+
+ def started?
+ STARTED_STATUSES.include?(status) && started_at
+ end
+
+ def active?
+ ACTIVE_STATUSES.include?(status)
+ end
+
+ def complete?
+ COMPLETED_STATUSES.include?(status)
+ end
+
+ def blocked?
+ BLOCKED_STATUS.include?(status)
+ end
+
+ private
+
+ def calculate_duration
+ if started_at && finished_at
+ finished_at - started_at
+ elsif started_at
+ Time.current - started_at
+ end
+ end
+ end
+end
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
deleted file mode 100644
index c885dea862f..00000000000
--- a/app/models/concerns/has_status.rb
+++ /dev/null
@@ -1,166 +0,0 @@
-# frozen_string_literal: true
-
-module HasStatus
- extend ActiveSupport::Concern
-
- DEFAULT_STATUS = 'created'
- BLOCKED_STATUS = %w[manual scheduled].freeze
- AVAILABLE_STATUSES = %w[created waiting_for_resource preparing pending running success failed canceled skipped manual scheduled].freeze
- STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
- ACTIVE_STATUSES = %w[waiting_for_resource preparing pending running].freeze
- COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
- ORDERED_STATUSES = %w[failed preparing pending running waiting_for_resource manual scheduled canceled success skipped created].freeze
- PASSED_WITH_WARNINGS_STATUSES = %w[failed canceled].to_set.freeze
- EXCLUDE_IGNORED_STATUSES = %w[manual failed canceled].to_set.freeze
- STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
- failed: 4, canceled: 5, skipped: 6, manual: 7,
- scheduled: 8, preparing: 9, waiting_for_resource: 10 }.freeze
-
- UnknownStatusError = Class.new(StandardError)
-
- class_methods do
- def legacy_status_sql
- scope_relevant = respond_to?(:exclude_ignored) ? exclude_ignored : all
- scope_warnings = respond_to?(:failed_but_allowed) ? failed_but_allowed : none
-
- builds = scope_relevant.select('count(*)').to_sql
- created = scope_relevant.created.select('count(*)').to_sql
- success = scope_relevant.success.select('count(*)').to_sql
- manual = scope_relevant.manual.select('count(*)').to_sql
- scheduled = scope_relevant.scheduled.select('count(*)').to_sql
- preparing = scope_relevant.preparing.select('count(*)').to_sql
- waiting_for_resource = scope_relevant.waiting_for_resource.select('count(*)').to_sql
- pending = scope_relevant.pending.select('count(*)').to_sql
- running = scope_relevant.running.select('count(*)').to_sql
- skipped = scope_relevant.skipped.select('count(*)').to_sql
- canceled = scope_relevant.canceled.select('count(*)').to_sql
- warnings = scope_warnings.select('count(*) > 0').to_sql.presence || 'false'
-
- Arel.sql(
- "(CASE
- WHEN (#{builds})=(#{skipped}) AND (#{warnings}) THEN 'success'
- WHEN (#{builds})=(#{skipped}) THEN 'skipped'
- WHEN (#{builds})=(#{success}) THEN 'success'
- WHEN (#{builds})=(#{created}) THEN 'created'
- WHEN (#{builds})=(#{preparing}) THEN 'preparing'
- WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success'
- WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled'
- WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
- WHEN (#{running})+(#{pending})>0 THEN 'running'
- WHEN (#{waiting_for_resource})>0 THEN 'waiting_for_resource'
- WHEN (#{manual})>0 THEN 'manual'
- WHEN (#{scheduled})>0 THEN 'scheduled'
- WHEN (#{preparing})>0 THEN 'preparing'
- WHEN (#{created})>0 THEN 'running'
- ELSE 'failed'
- END)"
- )
- end
-
- def legacy_status
- all.pluck(legacy_status_sql).first
- end
-
- # This method should not be used.
- # This method performs expensive calculation of status:
- # 1. By plucking all related objects,
- # 2. Or executes expensive SQL query
- def slow_composite_status(project:)
- if ::Gitlab::Ci::Features.composite_status?(project)
- Gitlab::Ci::Status::Composite
- .new(all, with_allow_failure: columns_hash.key?('allow_failure'))
- .status
- else
- legacy_status
- end
- end
-
- def started_at
- all.minimum(:started_at)
- end
-
- def finished_at
- all.maximum(:finished_at)
- end
-
- def all_state_names
- state_machines.values.flat_map(&:states).flat_map { |s| s.map(&:name) }
- end
-
- def completed_statuses
- COMPLETED_STATUSES.map(&:to_sym)
- end
- end
-
- included do
- validates :status, inclusion: { in: AVAILABLE_STATUSES }
-
- state_machine :status, initial: :created do
- state :created, value: 'created'
- state :waiting_for_resource, value: 'waiting_for_resource'
- state :preparing, value: 'preparing'
- state :pending, value: 'pending'
- state :running, value: 'running'
- state :failed, value: 'failed'
- state :success, value: 'success'
- state :canceled, value: 'canceled'
- state :skipped, value: 'skipped'
- state :manual, value: 'manual'
- state :scheduled, value: 'scheduled'
- end
-
- scope :created, -> { with_status(:created) }
- scope :waiting_for_resource, -> { with_status(:waiting_for_resource) }
- scope :preparing, -> { with_status(:preparing) }
- scope :relevant, -> { without_status(:created) }
- scope :running, -> { with_status(:running) }
- scope :pending, -> { with_status(:pending) }
- scope :success, -> { with_status(:success) }
- scope :failed, -> { with_status(:failed) }
- scope :canceled, -> { with_status(:canceled) }
- scope :skipped, -> { with_status(:skipped) }
- scope :manual, -> { with_status(:manual) }
- scope :scheduled, -> { with_status(:scheduled) }
- scope :alive, -> { with_status(:created, :waiting_for_resource, :preparing, :pending, :running) }
- scope :alive_or_scheduled, -> { with_status(:created, :waiting_for_resource, :preparing, :pending, :running, :scheduled) }
- scope :created_or_pending, -> { with_status(:created, :pending) }
- scope :running_or_pending, -> { with_status(:running, :pending) }
- scope :finished, -> { with_status(:success, :failed, :canceled) }
- scope :failed_or_canceled, -> { with_status(:failed, :canceled) }
- scope :incomplete, -> { without_statuses(completed_statuses) }
-
- scope :cancelable, -> do
- where(status: [:running, :waiting_for_resource, :preparing, :pending, :created, :scheduled])
- end
-
- scope :without_statuses, -> (names) do
- with_status(all_state_names - names.to_a)
- end
- end
-
- def started?
- STARTED_STATUSES.include?(status) && started_at
- end
-
- def active?
- ACTIVE_STATUSES.include?(status)
- end
-
- def complete?
- COMPLETED_STATUSES.include?(status)
- end
-
- def blocked?
- BLOCKED_STATUS.include?(status)
- end
-
- private
-
- def calculate_duration
- if started_at && finished_at
- finished_at - started_at
- elsif started_at
- Time.current - started_at
- end
- end
-end
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 8dae2d760f5..103d34f753d 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -147,7 +147,7 @@ class Environment < ApplicationRecord
Ci::Build.joins(inner_join_stop_actions)
.with(cte.to_arel)
.where(ci_builds[:commit_id].in(pipeline_ids))
- .where(status: HasStatus::BLOCKED_STATUS)
+ .where(status: Ci::HasStatus::BLOCKED_STATUS)
.preload_project_and_pipeline_project
.preload(:user, :metadata, :deployment)
end
diff --git a/app/models/project.rb b/app/models/project.rb
index e63eac02dca..1407c7b3820 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -2414,6 +2414,11 @@ class Project < ApplicationRecord
super || build_metrics_setting
end
+ def service_desk_enabled
+ false
+ end
+ alias_method :service_desk_enabled?, :service_desk_enabled
+
private
def find_service(services, name)
diff --git a/app/models/user.rb b/app/models/user.rb
index 99dfc9ee13d..68d8c449fd6 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -656,6 +656,15 @@ class User < ApplicationRecord
end
end
+ def support_bot
+ email_pattern = "support%s@#{Settings.gitlab.host}"
+
+ unique_internal(where(user_type: :support_bot), 'support-bot', email_pattern) do |u|
+ u.bio = 'The GitLab support bot used for Service Desk'
+ u.name = 'GitLab Support Bot'
+ end
+ end
+
# Return true if there is only single non-internal user in the deployment,
# ghost user is ignored.
def single_user?
diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb
index 2c26ba565ab..f41d69c7a41 100644
--- a/app/policies/base_policy.rb
+++ b/app/policies/base_policy.rb
@@ -21,6 +21,10 @@ class BasePolicy < DeclarativePolicy::Base
with_options scope: :user, score: 0
condition(:deactivated) { @user&.deactivated? }
+ desc "User is support bot"
+ with_options scope: :user, score: 0
+ condition(:support_bot) { @user&.support_bot? }
+
desc "User email is unconfirmed or user account is locked"
with_options scope: :user, score: 0
condition(:inactive) do
diff --git a/app/policies/concerns/policy_actor.rb b/app/policies/concerns/policy_actor.rb
index f910e04d015..3073a2e5d10 100644
--- a/app/policies/concerns/policy_actor.rb
+++ b/app/policies/concerns/policy_actor.rb
@@ -45,6 +45,10 @@ module PolicyActor
false
end
+ def support_bot?
+ false
+ end
+
def deactivated?
false
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 6ff16b71ea3..90f1f70d0de 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -123,6 +123,9 @@ class ProjectPolicy < BasePolicy
!@subject.design_management_enabled?
end
+ with_scope :subject
+ condition(:service_desk_enabled) { @subject.service_desk_enabled? }
+
# We aren't checking `:read_issue` or `:read_merge_request` in this case
# because it could be possible for a user to see an issuable-iid
# (`:read_issue_iid` or `:read_merge_request_iid`) but then wouldn't be
@@ -578,6 +581,12 @@ class ProjectPolicy < BasePolicy
enable :read_build_report_results
end
+ rule { support_bot }.enable :guest_access
+ rule { support_bot & ~service_desk_enabled }.policy do
+ prevent :create_note
+ prevent :read_project
+ end
+
private
def team_member?
@@ -626,6 +635,7 @@ class ProjectPolicy < BasePolicy
def lookup_access_level!
return ::Gitlab::Access::REPORTER if alert_bot?
+ return ::Gitlab::Access::REPORTER if support_bot? && service_desk_enabled?
# NOTE: max_member_access has its own cache
project.team.max_member_access(@user.id)
diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb
index 0b0454c5282..0aadcd01a43 100644
--- a/app/serializers/stage_entity.rb
+++ b/app/serializers/stage_entity.rb
@@ -59,13 +59,13 @@ class StageEntity < Grape::Entity
end
def latest_statuses
- HasStatus::ORDERED_STATUSES.flat_map do |ordered_status|
+ Ci::HasStatus::ORDERED_STATUSES.flat_map do |ordered_status|
grouped_statuses.fetch(ordered_status, [])
end
end
def retried_statuses
- HasStatus::ORDERED_STATUSES.flat_map do |ordered_status|
+ Ci::HasStatus::ORDERED_STATUSES.flat_map do |ordered_status|
grouped_retried_statuses.fetch(ordered_status, [])
end
end
diff --git a/app/services/authorized_project_update/periodic_recalculate_service.rb b/app/services/authorized_project_update/periodic_recalculate_service.rb
new file mode 100644
index 00000000000..91c0f50e5e0
--- /dev/null
+++ b/app/services/authorized_project_update/periodic_recalculate_service.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module AuthorizedProjectUpdate
+ class PeriodicRecalculateService
+ BATCH_SIZE = 480
+ DELAY_INTERVAL = 30.seconds.to_i
+
+ def execute
+ # Using this approach (instead of eg. User.each_batch) keeps the arguments
+ # the same for AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker
+ # even if the user list changes, so we can deduplicate these jobs.
+ (1..User.maximum(:id)).each_slice(BATCH_SIZE).with_index do |batch, index|
+ delay = DELAY_INTERVAL * index
+ AuthorizedProjectUpdate::UserRefreshOverUserRangeWorker.perform_in(delay, *batch.minmax)
+ end
+ end
+ end
+end
diff --git a/app/services/authorized_project_update/recalculate_for_user_range_service.rb b/app/services/authorized_project_update/recalculate_for_user_range_service.rb
new file mode 100644
index 00000000000..14b0f5d6117
--- /dev/null
+++ b/app/services/authorized_project_update/recalculate_for_user_range_service.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module AuthorizedProjectUpdate
+ class RecalculateForUserRangeService
+ def initialize(start_user_id, end_user_id)
+ @start_user_id = start_user_id
+ @end_user_id = end_user_id
+ end
+
+ def execute
+ User.where(id: start_user_id..end_user_id).select(:id).find_each do |user| # rubocop: disable CodeReuse/ActiveRecord
+ Users::RefreshAuthorizedProjectsService.new(user).execute
+ end
+ end
+
+ private
+
+ attr_reader :start_user_id, :end_user_id
+ end
+end
diff --git a/app/services/ci/pipeline_processing/atomic_processing_service.rb b/app/services/ci/pipeline_processing/atomic_processing_service.rb
index b01a9d2e3b8..a23d5d8941a 100644
--- a/app/services/ci/pipeline_processing/atomic_processing_service.rb
+++ b/app/services/ci/pipeline_processing/atomic_processing_service.rb
@@ -77,7 +77,7 @@ module Ci
def update_processable!(processable)
status = processable_status(processable)
- return unless HasStatus::COMPLETED_STATUSES.include?(status)
+ return unless Ci::HasStatus::COMPLETED_STATUSES.include?(status)
# transition status if possible
Gitlab::OptimisticLocking.retry_lock(processable) do |subject|
diff --git a/app/services/ci/pipeline_processing/atomic_processing_service/status_collection.rb b/app/services/ci/pipeline_processing/atomic_processing_service/status_collection.rb
index 2228328882d..d0aa8b04775 100644
--- a/app/services/ci/pipeline_processing/atomic_processing_service/status_collection.rb
+++ b/app/services/ci/pipeline_processing/atomic_processing_service/status_collection.rb
@@ -80,7 +80,7 @@ module Ci
# TODO: This is hack to support
# the same exact behaviour for Atomic and Legacy processing
# that DAG is blocked from executing if dependent is not "complete"
- if dag && statuses.any? { |status| HasStatus::COMPLETED_STATUSES.exclude?(status[:status]) }
+ if dag && statuses.any? { |status| Ci::HasStatus::COMPLETED_STATUSES.exclude?(status[:status]) }
return 'pending'
end
diff --git a/app/services/ci/pipeline_processing/legacy_processing_service.rb b/app/services/ci/pipeline_processing/legacy_processing_service.rb
index c471f7f0011..56fbc7271da 100644
--- a/app/services/ci/pipeline_processing/legacy_processing_service.rb
+++ b/app/services/ci/pipeline_processing/legacy_processing_service.rb
@@ -35,7 +35,7 @@ module Ci
def process_stage_for_stage_scheduling(index)
current_status = status_for_prior_stages(index)
- return unless HasStatus::COMPLETED_STATUSES.include?(current_status)
+ return unless Ci::HasStatus::COMPLETED_STATUSES.include?(current_status)
created_stage_scheduled_processables_in_stage(index).find_each.select do |build|
process_build(build, current_status)
@@ -73,7 +73,7 @@ module Ci
def process_dag_build_with_needs(build)
current_status = status_for_build_needs(build.needs.map(&:name))
- return unless HasStatus::COMPLETED_STATUSES.include?(current_status)
+ return unless Ci::HasStatus::COMPLETED_STATUSES.include?(current_status)
process_build(build, current_status)
end
diff --git a/app/services/projects/update_repository_storage_service.rb b/app/services/projects/update_repository_storage_service.rb
index fa8d4c5aa5f..f1a58562190 100644
--- a/app/services/projects/update_repository_storage_service.rb
+++ b/app/services/projects/update_repository_storage_service.rb
@@ -93,25 +93,25 @@ module Projects
old_repository_storage = project.repository_storage
new_project_path = moved_path(project.disk_path)
- # Notice that the block passed to `run_after_commit` will run with `project`
+ # Notice that the block passed to `run_after_commit` will run with `repository_storage_move`
# as its context
- project.run_after_commit do
+ repository_storage_move.run_after_commit do
GitlabShellWorker.perform_async(:mv_repository,
old_repository_storage,
- disk_path,
+ project.disk_path,
new_project_path)
- if wiki.repository_exists?
+ if project.wiki.repository_exists?
GitlabShellWorker.perform_async(:mv_repository,
old_repository_storage,
- wiki.disk_path,
+ project.wiki.disk_path,
"#{new_project_path}.wiki")
end
- if design_repository.exists?
+ if project.design_repository.exists?
GitlabShellWorker.perform_async(:mv_repository,
old_repository_storage,
- design_repository.disk_path,
+ project.design_repository.disk_path,
"#{new_project_path}.design")
end
end
diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb
index 0e7a4821bdf..621266f00e1 100644
--- a/app/services/users/refresh_authorized_projects_service.rb
+++ b/app/services/users/refresh_authorized_projects_service.rb
@@ -85,8 +85,6 @@ module Users
# remove - The IDs of the authorization rows to remove.
# add - Rows to insert in the form `[user id, project id, access level]`
def update_authorizations(remove = [], add = [])
- return if remove.empty? && add.empty?
-
User.transaction do
user.remove_project_authorizations(remove) unless remove.empty?
ProjectAuthorization.insert_authorizations(add) unless add.empty?
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index aa47daf4a57..c7efc4955ab 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -100,7 +100,7 @@
.hint
= parsed_with_gfm
- .prepend-top-default.append-bottom-default
+ .prepend-top-default.gl-mb-3
= f.submit 'Update appearance settings', class: 'btn btn-success'
- if @appearance.persisted? || @appearance.updated_at
.mt-4
diff --git a/app/views/admin/application_settings/_signin.html.haml b/app/views/admin/application_settings/_signin.html.haml
index 007cd343339..0972e10e12c 100644
--- a/app/views/admin/application_settings/_signin.html.haml
+++ b/app/views/admin/application_settings/_signin.html.haml
@@ -33,6 +33,15 @@
= f.label :require_two_factor_authentication, class: 'form-check-label' do
Require all users to set up Two-factor authentication
.form-group
+ = f.label :unknown_sign_in, _('Email notification for unknown sign-ins'), class: 'label-bold'
+ .form-check
+ = f.check_box :notify_on_unknown_sign_in, class: 'form-check-input'
+ = f.label :notify_on_unknown_sign_in, class: 'form-check-label' do
+ = _('Notify users by email when sign-in location is not recognized')
+ = link_to icon('question-circle'),
+ 'https://docs.gitlab.com/ee/user/profile/unknown_sign_in_notification.html',
+ target: '_blank'
+ .form-group
= f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'label-bold'
= f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0'
.form-text.text-muted Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
diff --git a/app/views/admin/groups/index.html.haml b/app/views/admin/groups/index.html.haml
index f295e5a06cb..ccb3f8594d4 100644
--- a/app/views/admin/groups/index.html.haml
+++ b/app/views/admin/groups/index.html.haml
@@ -1,7 +1,7 @@
- page_title _("Groups")
.top-area
- .prepend-top-default.append-bottom-default
+ .prepend-top-default.gl-mb-3
= form_tag admin_groups_path, method: :get, class: 'js-search-form' do |f|
= hidden_field_tag :sort, @sort
.search-holder
diff --git a/app/views/admin/hook_logs/_index.html.haml b/app/views/admin/hook_logs/_index.html.haml
index 841640efad2..eeeb58f9c76 100644
--- a/app/views/admin/hook_logs/_index.html.haml
+++ b/app/views/admin/hook_logs/_index.html.haml
@@ -1,4 +1,4 @@
-.row.prepend-top-default.append-bottom-default
+.row.prepend-top-default.gl-mb-3
.col-lg-3
%h4.gl-mt-0
Recent Deliveries
diff --git a/app/views/admin/hooks/edit.html.haml b/app/views/admin/hooks/edit.html.haml
index 636dd6bdfc1..360b85fd451 100644
--- a/app/views/admin/hooks/edit.html.haml
+++ b/app/views/admin/hooks/edit.html.haml
@@ -5,7 +5,7 @@
.col-lg-3
= render 'shared/web_hooks/title_and_docs', hook: @hook
- .col-lg-9.append-bottom-default
+ .col-lg-9.gl-mb-3
= form_for @hook, as: :hook, url: admin_hook_path do |f|
= render partial: 'form', locals: { form: f, hook: @hook }
.form-actions
diff --git a/app/views/admin/hooks/index.html.haml b/app/views/admin/hooks/index.html.haml
index 1c14291b58e..1d2850b2bee 100644
--- a/app/views/admin/hooks/index.html.haml
+++ b/app/views/admin/hooks/index.html.haml
@@ -4,7 +4,7 @@
.col-lg-4
= render 'shared/web_hooks/title_and_docs', hook: @hook
- .col-lg-8.append-bottom-default
+ .col-lg-8.gl-mb-3
= form_for @hook, as: :hook, url: admin_hooks_path do |f|
= render partial: 'form', locals: { form: f, hook: @hook }
= f.submit _('Add system hook'), class: 'btn btn-success'
diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml
index a218885a00e..3403e9e5abf 100644
--- a/app/views/admin/users/_head.html.haml
+++ b/app/views/admin/users/_head.html.haml
@@ -28,4 +28,4 @@
= link_to "Identities", admin_user_identities_path(@user)
= nav_link(controller: :impersonation_tokens) do
= link_to "Impersonation Tokens", admin_user_impersonation_tokens_path(@user)
-.append-bottom-default
+.gl-mb-3
diff --git a/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml b/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
index 486625c790b..b9fff03b938 100644
--- a/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
+++ b/app/views/clusters/clusters/_gcp_signup_offer_banner.html.haml
@@ -1,5 +1,5 @@
- link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
-.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert', data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } }
+.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.gl-mb-3{ role: 'alert', data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } }
%button.close.js-close{ type: "button" } &times;
.gcp-signup-offer--content
.gcp-signup-offer--icon.gl-mr-3
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index 9aab1556373..1ed59e97315 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -49,7 +49,7 @@
- else
.settings-message.text-center
= _("You don't have any applications")
- .oauth-authorized-applications.prepend-top-20.append-bottom-default
+ .oauth-authorized-applications.prepend-top-20.gl-mb-3
- if user_oauth_applications?
%h5
= _("Authorized applications (%{size})") % { size: @authorized_apps.size + @authorized_anonymous_tokens.size }
diff --git a/app/views/groups/settings/_lfs.html.haml b/app/views/groups/settings/_lfs.html.haml
index 7970c3c73f6..77c84862316 100644
--- a/app/views/groups/settings/_lfs.html.haml
+++ b/app/views/groups/settings/_lfs.html.haml
@@ -5,7 +5,7 @@
%p= s_('Check the %{docs_link_start}documentation%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: '</a>'.html_safe }
-.form-group.append-bottom-default
+.form-group.gl-mb-3
.form-check
= f.check_box :lfs_enabled, checked: @group.lfs_enabled?, class: 'form-check-input', data: { qa_selector: 'lfs_checkbox' }
= f.label :lfs_enabled, class: 'form-check-label' do
diff --git a/app/views/groups/settings/_permissions.html.haml b/app/views/groups/settings/_permissions.html.haml
index e886c99a656..4f5fb0afb1f 100644
--- a/app/views/groups/settings/_permissions.html.haml
+++ b/app/views/groups/settings/_permissions.html.haml
@@ -7,7 +7,7 @@
.form-group
= render 'shared/allow_request_access', form: f
- .form-group.append-bottom-default
+ .form-group.gl-mb-3
.form-check
= f.check_box :share_with_group_lock, disabled: !can_change_share_with_group_lock?(@group), class: 'form-check-input'
= f.label :share_with_group_lock, class: 'form-check-label' do
@@ -16,14 +16,14 @@
= s_('GroupSettings|Prevent sharing a project within %{group} with other groups').html_safe % { group: group_link }
%span.js-descr.text-muted= share_with_group_lock_help_text(@group)
- .form-group.append-bottom-default
+ .form-group.gl-mb-3
.form-check
= f.check_box :emails_disabled, checked: @group.emails_disabled?, disabled: !can_disable_group_emails?(@group), class: 'form-check-input'
= f.label :emails_disabled, class: 'form-check-label' do
%span.d-block= s_('GroupSettings|Disable email notifications')
%span.text-muted= s_('GroupSettings|This setting will override user notification preferences for all members of the group, subgroups, and projects.')
- .form-group.append-bottom-default
+ .form-group.gl-mb-3
.form-check
= f.check_box :mentions_disabled, checked: @group.mentions_disabled?, class: 'form-check-input'
= f.label :mentions_disabled, class: 'form-check-label' do
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index f4a97206a19..e884d2d5df6 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -72,4 +72,4 @@
- else
%p
= s_("Profiles|You don't have access to delete this user.")
-.append-bottom-default
+.gl-mb-3
diff --git a/app/views/profiles/active_sessions/index.html.haml b/app/views/profiles/active_sessions/index.html.haml
index 6d01d055f0c..62538908d8f 100644
--- a/app/views/profiles/active_sessions/index.html.haml
+++ b/app/views/profiles/active_sessions/index.html.haml
@@ -8,7 +8,7 @@
%p
= _('This is a list of devices that have logged into your account. Revoke any sessions that you do not recognize.')
.col-lg-8
- .append-bottom-default
+ .gl-mb-3
.card.border-0
%ul.list-group.list-group-flush
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index e90bda0e187..4b24fc7288a 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -19,7 +19,7 @@
%hr
%h4.gl-mt-0
= _('Linked emails (%{email_count})') % { email_count: @emails.load.size + 1 }
- .account-well.append-bottom-default
+ .account-well.gl-mb-3
%ul
%li
= _('Your Primary Email will be used for avatar detection.')
diff --git a/app/views/profiles/gpg_keys/index.html.haml b/app/views/profiles/gpg_keys/index.html.haml
index 31610e7505b..852c67ed78f 100644
--- a/app/views/profiles/gpg_keys/index.html.haml
+++ b/app/views/profiles/gpg_keys/index.html.haml
@@ -17,5 +17,5 @@
%hr
%h5
= _('Your GPG keys (%{count})') % { count:@gpg_keys.count}
- .append-bottom-default
+ .gl-mb-3
= render 'key_table'
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index 788c67b3704..f11a54bcbaa 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -20,5 +20,5 @@
%hr
%h5
= _('Your SSH keys (%{count})') % { count:@keys.count }
- .append-bottom-default
+ .gl-mb-3
= render 'key_table'
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 498f80aed2b..522522c5023 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -47,7 +47,7 @@
= _('Projects (%{count})') % { count: @project_notifications.size }
%p.account-well
= _('To specify the notification level per project of a group you belong to, you need to visit project page and change notification level there.')
- .append-bottom-default
+ .gl-mb-3
%ul.bordered-list
- @project_notifications.each do |setting|
= render 'project_settings', setting: setting, project: setting.source
diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml
index 9deaf7f84be..b6653048367 100644
--- a/app/views/profiles/passwords/edit.html.haml
+++ b/app/views/profiles/passwords/edit.html.haml
@@ -29,7 +29,7 @@
.form-group
= f.label :password_confirmation, _('Password confirmation'), class: 'label-bold'
= f.password_field :password_confirmation, required: true, class: 'form-control', data: { qa_selector: 'confirm_password_field' }
- .prepend-top-default.append-bottom-default
+ .prepend-top-default.gl-mb-3
= f.submit _('Save password'), class: "btn btn-success append-right-10", data: { qa_selector: 'save_password_button' }
- unless @user.password_automatically_set?
= link_to _('I forgot my password'), reset_profile_password_path, method: :put
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index f5ea805cf85..c7c01f0b623 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -25,7 +25,7 @@
.md
= brand_profile_image_guidelines
.col-lg-8
- .clearfix.avatar-image.append-bottom-default
+ .clearfix.avatar-image.gl-mb-3
= link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
= image_tag avatar_icon_for_user(@user, 160), alt: '', class: 'avatar s160'
%h5.gl-mt-0= s_("Profiles|Upload new avatar")
@@ -118,7 +118,7 @@
= f.check_box :include_private_contributions, label: s_('Profiles|Include private contributions on my profile'), wrapper_class: 'mb-2', inline: true
.help-block
= s_("Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information")
- .prepend-top-default.append-bottom-default
+ .prepend-top-default.gl-mb-3
= f.submit s_("Profiles|Update profile settings"), class: 'btn btn-success'
= link_to _("Cancel"), user_path(current_user), class: 'btn btn-cancel'
diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml
index 5d2ceacfe3e..47f91f719bc 100644
--- a/app/views/projects/_files.html.haml
+++ b/app/views/projects/_files.html.haml
@@ -18,7 +18,7 @@
= render 'shared/commit_well', commit: commit, ref: ref, project: project
- if is_project_overview
- .project-buttons.append-bottom-default{ class: ("js-show-on-project-root" if vue_file_list_enabled?) }
+ .project-buttons.gl-mb-3{ class: ("js-show-on-project-root" if vue_file_list_enabled?) }
= render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
- if vue_file_list_enabled?
diff --git a/app/views/projects/_wiki.html.haml b/app/views/projects/_wiki.html.haml
index 6f90bf50b91..761af314469 100644
--- a/app/views/projects/_wiki.html.haml
+++ b/app/views/projects/_wiki.html.haml
@@ -1,6 +1,6 @@
- if @wiki_home.present?
%div{ class: container_class }
- .md.prepend-top-default.append-bottom-default
+ .md.prepend-top-default.gl-mb-3
= render_wiki_content(@wiki_home)
- else
- can_create_wiki = can?(current_user, :create_wiki, @project)
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index 67c78620f15..d8158376009 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -2,7 +2,7 @@
- file_name = params[:id].split("/").last ||= ""
- is_markdown = Gitlab::MarkupHelper.gitlab_markdown?(file_name)
-.file-holder-bottom-radius.file-holder.file.append-bottom-default
+.file-holder-bottom-radius.file-holder.file.gl-mb-3
.js-file-title.file-title.align-items-center.clearfix{ data: { current_action: action } }
.editor-ref.block-truncated
= sprite_icon('fork', size: 12)
diff --git a/app/views/projects/blob/viewers/_loading.html.haml b/app/views/projects/blob/viewers/_loading.html.haml
index df1f3e4e01b..606db3657a6 100644
--- a/app/views/projects/blob/viewers/_loading.html.haml
+++ b/app/views/projects/blob/viewers/_loading.html.haml
@@ -1,2 +1,2 @@
-.text-center.prepend-top-default.append-bottom-default
+.text-center.prepend-top-default.gl-mb-3
= icon('spinner spin 2x', 'aria-hidden' => 'true', 'aria-label' => 'Loading content…', class: 'qa-spinner')
diff --git a/app/views/projects/blob/viewers/_sketch.html.haml b/app/views/projects/blob/viewers/_sketch.html.haml
index b4b6492b92f..22b08a85b8d 100644
--- a/app/views/projects/blob/viewers/_sketch.html.haml
+++ b/app/views/projects/blob/viewers/_sketch.html.haml
@@ -1,3 +1,3 @@
.file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } }
- .js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' }
+ .js-loading-icon.text-center.prepend-top-default.gl-mb-3.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' }
= icon('spinner spin 2x', 'aria-hidden' => 'true');
diff --git a/app/views/projects/blob/viewers/_stl.html.haml b/app/views/projects/blob/viewers/_stl.html.haml
index 55dd8cba7fe..cbae6057630 100644
--- a/app/views/projects/blob/viewers/_stl.html.haml
+++ b/app/views/projects/blob/viewers/_stl.html.haml
@@ -1,7 +1,7 @@
.file-content.is-stl-loading
.text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } }
- = icon('spinner spin 2x', class: 'prepend-top-default append-bottom-default', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
- .text-center.prepend-top-default.append-bottom-default.stl-controls
+ = icon('spinner spin 2x', class: 'prepend-top-default gl-mb-3', 'aria-hidden' => 'true', 'aria-label' => 'Loading')
+ .text-center.prepend-top-default.gl-mb-3.stl-controls
.btn-group
%button.btn.btn-default.btn-sm.js-material-changer{ data: { type: 'wireframe' } }
Wireframe
diff --git a/app/views/projects/environments/_form.html.haml b/app/views/projects/environments/_form.html.haml
index efe80a4877c..f49431932f1 100644
--- a/app/views/projects/environments/_form.html.haml
+++ b/app/views/projects/environments/_form.html.haml
@@ -1,4 +1,4 @@
-.row.prepend-top-default.append-bottom-default
+.row.prepend-top-default.gl-mb-3
.col-lg-3
%h4.gl-mt-0
= _("Environments")
diff --git a/app/views/projects/forks/_fork_button.html.haml b/app/views/projects/forks/_fork_button.html.haml
index 70064722832..1b20a89ecb0 100644
--- a/app/views/projects/forks/_fork_button.html.haml
+++ b/app/views/projects/forks/_fork_button.html.haml
@@ -2,7 +2,7 @@
- can_create_project = current_user.can?(:create_projects, namespace)
- if forked_project = namespace.find_fork_of(@project)
- .bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.append-bottom-default.forked
+ .bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.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")
@@ -12,7 +12,7 @@
%h5.prepend-top-default
= namespace.human_name
- else
- .bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.append-bottom-default{ class: ("disabled" unless can_create_project) }
+ .bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.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),
diff --git a/app/views/projects/hook_logs/_index.html.haml b/app/views/projects/hook_logs/_index.html.haml
index e7b924c65bf..a8a4eef65b3 100644
--- a/app/views/projects/hook_logs/_index.html.haml
+++ b/app/views/projects/hook_logs/_index.html.haml
@@ -1,4 +1,4 @@
-.row.gl-mt-7.append-bottom-default
+.row.gl-mt-7.gl-mb-3
.col-lg-3
%h4.gl-mt-0
Recent Deliveries
diff --git a/app/views/projects/hook_logs/show.html.haml b/app/views/projects/hook_logs/show.html.haml
index a6a3f56c28c..37330788ca4 100644
--- a/app/views/projects/hook_logs/show.html.haml
+++ b/app/views/projects/hook_logs/show.html.haml
@@ -2,7 +2,7 @@
- add_to_breadcrumbs _('Webhook Settings'), namespace_project_hooks_path
- page_title _('Webhook Logs')
-.row.prepend-top-default.append-bottom-default
+.row.prepend-top-default.gl-mb-3
.col-lg-3
%h4.gl-mt-0
Request details
diff --git a/app/views/projects/hooks/edit.html.haml b/app/views/projects/hooks/edit.html.haml
index 15100840c0a..066cf183ac9 100644
--- a/app/views/projects/hooks/edit.html.haml
+++ b/app/views/projects/hooks/edit.html.haml
@@ -6,7 +6,7 @@
.col-lg-3
= render 'shared/web_hooks/title_and_docs', hook: @hook
- .col-lg-9.append-bottom-default
+ .col-lg-9.gl-mb-3
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: project_hook_path(@project, @hook) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
diff --git a/app/views/projects/hooks/index.html.haml b/app/views/projects/hooks/index.html.haml
index 169a5cc9d6b..56e60e747b2 100644
--- a/app/views/projects/hooks/index.html.haml
+++ b/app/views/projects/hooks/index.html.haml
@@ -6,7 +6,7 @@
.col-lg-4
= render 'shared/web_hooks/title_and_docs', hook: @hook
- .col-lg-8.append-bottom-default
+ .col-lg-8.gl-mb-3
= form_for @hook, as: :hook, url: polymorphic_path([@project.namespace.becomes(Namespace), @project, :hooks]) do |f|
= render partial: 'shared/web_hooks/form', locals: { form: f, hook: @hook }
= f.submit 'Add webhook', class: 'btn btn-success'
diff --git a/app/views/projects/issues/_by_email_description.html.haml b/app/views/projects/issues/_by_email_description.html.haml
index f2d58534903..0ff852352e1 100644
--- a/app/views/projects/issues/_by_email_description.html.haml
+++ b/app/views/projects/issues/_by_email_description.html.haml
@@ -1,6 +1,6 @@
The subject will be used as the title of the new issue, and the message will be the description.
-= link_to 'Quick actions', help_page_path('user/project/quick_actions'), target: '_blank', tabindex: -1
+= link_to 'Quick actions', help_page_path('user/project/quick_actions'), target: '_blank'
and styling with
-= link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1
+= link_to 'Markdown', help_page_path('user/markdown'), target: '_blank'
are supported.
diff --git a/app/views/projects/mirrors/_instructions.html.haml b/app/views/projects/mirrors/_instructions.html.haml
index 7ff6c0a2019..ebd43d2153b 100644
--- a/app/views/projects/mirrors/_instructions.html.haml
+++ b/app/views/projects/mirrors/_instructions.html.haml
@@ -1,4 +1,4 @@
-.account-well.prepend-top-default.append-bottom-default
+.account-well.prepend-top-default.gl-mb-3
%ul
%li
= _('The repository must be accessible over <code>http://</code>,
diff --git a/app/views/projects/pipelines/_stage.html.haml b/app/views/projects/pipelines/_stage.html.haml
index 3feb99cfcd7..0651ad6fdb8 100644
--- a/app/views/projects/pipelines/_stage.html.haml
+++ b/app/views/projects/pipelines/_stage.html.haml
@@ -1,5 +1,5 @@
- grouped_statuses = @stage.statuses.latest_ordered.group_by(&:status)
-- HasStatus::ORDERED_STATUSES.each do |ordered_status|
+- Ci::HasStatus::ORDERED_STATUSES.each do |ordered_status|
- grouped_statuses.fetch(ordered_status, []).each do |status|
%li
= render 'ci/status/dropdown_graph_badge', subject: status
diff --git a/app/views/projects/protected_branches/show.html.haml b/app/views/projects/protected_branches/show.html.haml
index 06578c94079..0e22b1e3105 100644
--- a/app/views/projects/protected_branches/show.html.haml
+++ b/app/views/projects/protected_branches/show.html.haml
@@ -1,6 +1,6 @@
- page_title @protected_ref.name, _("Protected Branches")
-.row.prepend-top-default.append-bottom-default
+.row.prepend-top-default.gl-mb-3
.col-lg-3
%h4.gl-mt-0.ref-name
= @protected_ref.name
diff --git a/app/views/projects/protected_tags/show.html.haml b/app/views/projects/protected_tags/show.html.haml
index 227aac77583..aad9e5a9340 100644
--- a/app/views/projects/protected_tags/show.html.haml
+++ b/app/views/projects/protected_tags/show.html.haml
@@ -1,6 +1,6 @@
- page_title @protected_ref.name, _("Protected Tags")
-.row.prepend-top-default.append-bottom-default
+.row.prepend-top-default.gl-mb-3
.col-lg-3
%h4.gl-mt-0.ref-name
= @protected_ref.name
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 91aa148dcb3..dd0a5aeb043 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -1,4 +1,4 @@
-.row.prepend-top-default.append-bottom-default
+.row.prepend-top-default.gl-mb-3
.col-lg-4
%h4.gl-mt-0
= @service.title
diff --git a/app/views/projects/services/prometheus/_help.html.haml b/app/views/projects/services/prometheus/_help.html.haml
index 1b5b794a7aa..c5b3fd31efa 100644
--- a/app/views/projects/services/prometheus/_help.html.haml
+++ b/app/views/projects/services/prometheus/_help.html.haml
@@ -1,7 +1,7 @@
- if @project
= render 'projects/services/prometheus/configuration_banner', project: @project, service: @service
-%h4.append-bottom-default
+%h4.gl-mb-3
= s_('PrometheusService|Manual configuration')
%p
= s_('PrometheusService|Select the Active checkbox to override the Auto Configuration with custom settings. If unchecked, Auto Configuration settings are used.')
diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml
index 728a52f024f..9ce61ed5c13 100644
--- a/app/views/projects/services/prometheus/_show.html.haml
+++ b/app/views/projects/services/prometheus/_show.html.haml
@@ -3,7 +3,7 @@
%h4.gl-mt-0
= s_('PrometheusService|Metrics')
-.row.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring
+.row.gl-mb-3.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring
= render 'projects/services/prometheus/metrics', project: @project
= render 'projects/services/prometheus/external_alerts', project: @project
diff --git a/app/views/projects/settings/operations/_prometheus.html.haml b/app/views/projects/settings/operations/_prometheus.html.haml
index b0fa750e131..7ccc829662d 100644
--- a/app/views/projects/settings/operations/_prometheus.html.haml
+++ b/app/views/projects/settings/operations/_prometheus.html.haml
@@ -11,7 +11,7 @@
- if @project
= render 'projects/settings/operations/configuration_banner', project: @project, service: service
- %b.append-bottom-default
+ %b.gl-mb-3
= s_('PrometheusService|Manual configuration')
%p
= s_('PrometheusService|Select the Active checkbox to override the Auto Configuration with custom settings. If unchecked, Auto Configuration settings are used.')
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index b98906a6863..a3cbee98dcc 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -59,7 +59,7 @@
%pre.wrap{ data: { qa_selector: 'tag_message_content' } }
= strip_signature(@tag.message)
-.append-bottom-default.prepend-top-default
+.gl-mb-3.prepend-top-default
- if @release.description.present?
.description.md{ data: { qa_selector: 'tag_release_notes_content' } }
= markdown_field(@release, :description)
diff --git a/app/views/projects/triggers/_index.html.haml b/app/views/projects/triggers/_index.html.haml
index 4ca070cb162..53f55442b01 100644
--- a/app/views/projects/triggers/_index.html.haml
+++ b/app/views/projects/triggers/_index.html.haml
@@ -1,4 +1,4 @@
-.row.prepend-top-default.append-bottom-default.triggers-container
+.row.prepend-top-default.gl-mb-3.triggers-container
.col-lg-12
.card
.card-header
@@ -21,7 +21,7 @@
%th
= render partial: 'projects/triggers/trigger', collection: @triggers, as: :trigger
- else
- %p.settings-message.text-center.append-bottom-default
+ %p.settings-message.text-center.gl-mb-3
No triggers have been created yet. Add one using the form above.
.card-footer
diff --git a/app/views/projects/triggers/edit.html.haml b/app/views/projects/triggers/edit.html.haml
index b1bc9b0f900..67ca4994146 100644
--- a/app/views/projects/triggers/edit.html.haml
+++ b/app/views/projects/triggers/edit.html.haml
@@ -1,6 +1,6 @@
- page_title _("Trigger")
-.row.prepend-top-default.append-bottom-default
+.row.prepend-top-default.gl-mb-3
.col-lg-12
%h4.gl-mt-0
Update trigger
diff --git a/app/views/shared/_commit_well.html.haml b/app/views/shared/_commit_well.html.haml
index 6f1fe9bfdc5..48fe258d01f 100644
--- a/app/views/shared/_commit_well.html.haml
+++ b/app/views/shared/_commit_well.html.haml
@@ -1,4 +1,4 @@
-.info-well.d-none.d-sm-block.project-last-commit.append-bottom-default
+.info-well.d-none.d-sm-block.project-last-commit.gl-mb-3
.well-segment
%ul.blob-commit-info
= render 'projects/commits/commit', commit: commit, ref: ref, project: project
diff --git a/app/views/shared/_md_preview.html.haml b/app/views/shared/_md_preview.html.haml
index f5f24b2f0ce..c3818b9f7ae 100644
--- a/app/views/shared/_md_preview.html.haml
+++ b/app/views/shared/_md_preview.html.haml
@@ -11,10 +11,10 @@
.md-header
%ul.nav.nav-tabs.nav-links.clearfix
%li.md-header-tab.active
- %button.js-md-write-button{ tabindex: -1 }
+ %button.js-md-write-button
= _("Write")
%li.md-header-tab
- %button.js-md-preview-button{ tabindex: -1 }
+ %button.js-md-preview-button
= _("Preview")
%li.md-header-toolbar.active
diff --git a/app/views/shared/file_hooks/_index.html.haml b/app/views/shared/file_hooks/_index.html.haml
index 436bd305df1..3b585f037df 100644
--- a/app/views/shared/file_hooks/_index.html.haml
+++ b/app/views/shared/file_hooks/_index.html.haml
@@ -9,7 +9,7 @@
= link_to _('For more information, see the File Hooks documentation.'), help_page_path('administration/file_hooks')
- .col-lg-8.append-bottom-default
+ .col-lg-8.gl-mb-3
- if file_hooks.any?
.card
.card-header
diff --git a/app/views/shared/issuable/form/_default_templates.html.haml b/app/views/shared/issuable/form/_default_templates.html.haml
index 49a5ce926b3..3dc244677e2 100644
--- a/app/views/shared/issuable/form/_default_templates.html.haml
+++ b/app/views/shared/issuable/form/_default_templates.html.haml
@@ -1,4 +1,4 @@
%p.form-text.text-muted
Add
- = link_to 'description templates', help_page_path('user/project/description_templates'), tabindex: -1
+ = link_to 'description templates', help_page_path('user/project/description_templates')
to help your contributors communicate effectively!
diff --git a/app/views/shared/issuable/form/_merge_params.html.haml b/app/views/shared/issuable/form/_merge_params.html.haml
index 1b557214e02..2cd4e0ba761 100644
--- a/app/views/shared/issuable/form/_merge_params.html.haml
+++ b/app/views/shared/issuable/form/_merge_params.html.haml
@@ -9,7 +9,7 @@
= _('Merge options')
.col-sm-10
- if issuable.can_remove_source_branch?(current_user)
- .form-check.append-bottom-default
+ .form-check.gl-mb-3
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil
= check_box_tag 'merge_request[force_remove_source_branch]', '1', issuable.force_remove_source_branch?, class: 'form-check-input'
= label_tag 'merge_request[force_remove_source_branch]', class: 'form-check-label' do
diff --git a/app/views/shared/issuable/form/_title.html.haml b/app/views/shared/issuable/form/_title.html.haml
index 75e9ab547ce..355a6627b8f 100644
--- a/app/views/shared/issuable/form/_title.html.haml
+++ b/app/views/shared/issuable/form/_title.html.haml
@@ -11,7 +11,7 @@
- if issuable.respond_to?(:work_in_progress?)
.form-text.text-muted
.js-wip-explanation
- %a.js-toggle-wip{ href: '', tabindex: -1 }
+ %a.js-toggle-wip{ href: '' }
Remove the
%code WIP:
prefix from the title
@@ -22,7 +22,7 @@
- if has_wip_commits
It looks like you have some WIP commits in this branch.
%br
- %a.js-toggle-wip{ href: '', tabindex: -1 }
+ %a.js-toggle-wip{ href: '' }
Start the title with
%code WIP:
to prevent a
diff --git a/app/views/shared/notes/_hints.html.haml b/app/views/shared/notes/_hints.html.haml
index 902a6e9b363..1dca2cffea6 100644
--- a/app/views/shared/notes/_hints.html.haml
+++ b/app/views/shared/notes/_hints.html.haml
@@ -1,10 +1,10 @@
- supports_quick_actions = local_assigns.fetch(:supports_quick_actions, false)
.comment-toolbar.clearfix
.toolbar-text
- = link_to _('Markdown'), help_page_path('user/markdown'), target: '_blank', tabindex: -1
+ = link_to _('Markdown'), help_page_path('user/markdown'), target: '_blank'
- if supports_quick_actions
and
- = link_to _('quick actions'), help_page_path('user/project/quick_actions'), target: '_blank', tabindex: -1
+ = link_to _('quick actions'), help_page_path('user/project/quick_actions'), target: '_blank'
are
- else
is
@@ -28,7 +28,7 @@
or
%button.attach-new-file.markdown-selector{ type: 'button' }= _("attach a new file")
- %button.markdown-selector.button-attach-file.btn-link{ type: 'button', tabindex: '-1' }
+ %button.markdown-selector.button-attach-file.btn-link{ type: 'button' }
= icon('file-image-o', class: 'toolbar-button-icon')
%span.text-attach-file<>
= _("Attach a file")
diff --git a/app/views/shared/web_hooks/_index.html.haml b/app/views/shared/web_hooks/_index.html.haml
index 149f4baeb21..51c99d9699b 100644
--- a/app/views/shared/web_hooks/_index.html.haml
+++ b/app/views/shared/web_hooks/_index.html.haml
@@ -10,5 +10,5 @@
- hooks.each do |hook|
= render 'shared/web_hooks/hook', hook: hook
- else
- %p.text-center.prepend-top-default.append-bottom-default
+ %p.text-center.prepend-top-default.gl-mb-3
= _('No webhooks found, add one in the form above.')
diff --git a/app/views/shared/wikis/_sidebar.html.haml b/app/views/shared/wikis/_sidebar.html.haml
index 8cfb95cdcf5..fd3d1e1e06a 100644
--- a/app/views/shared/wikis/_sidebar.html.haml
+++ b/app/views/shared/wikis/_sidebar.html.haml
@@ -1,6 +1,6 @@
%aside.right-sidebar.right-sidebar-expanded.wiki-sidebar.js-wiki-sidebar.js-right-sidebar{ data: { "offset-top" => "50", "spy" => "affix" } }
.sidebar-container
- .block.wiki-sidebar-header.append-bottom-default.w-100
+ .block.wiki-sidebar-header.gl-mb-3.w-100
%a.gutter-toggle.float-right.d-block.d-sm-block.d-md-none.js-sidebar-wiki-toggle{ href: "#" }
= icon('angle-double-right')
diff --git a/app/views/shared/wikis/show.html.haml b/app/views/shared/wikis/show.html.haml
index a4f3996e5de..00beca36855 100644
--- a/app/views/shared/wikis/show.html.haml
+++ b/app/views/shared/wikis/show.html.haml
@@ -25,7 +25,7 @@
- history_link = link_to s_("WikiHistoricalPage|history"), wiki_page_path(@wiki, @page, action: :history)
= (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe
-.prepend-top-default.append-bottom-default
+.prepend-top-default.gl-mb-3
.md{ data: { qa_selector: 'wiki_page_content' } }
= render_wiki_content(@page)
diff --git a/app/views/users/_overview.html.haml b/app/views/users/_overview.html.haml
index 7bd2d30a35c..e1277d3723d 100644
--- a/app/views/users/_overview.html.haml
+++ b/app/views/users/_overview.html.haml
@@ -1,6 +1,6 @@
.row
.col-12
- .calendar-block.prepend-top-default.append-bottom-default
+ .calendar-block.prepend-top-default.gl-mb-3
.user-calendar.d-none.d-sm-block{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
%h4.center.light
.spinner.spinner-md
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 3a25ce2e870..8750223e2bf 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -5,7 +5,7 @@
---
- :name: authorized_project_update:authorized_project_update_project_create
:feature_category: :authentication_and_authorization
- :has_external_dependencies:
+ :has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
:weight: 1
@@ -13,6 +13,14 @@
:tags: []
- :name: authorized_project_update:authorized_project_update_project_group_link_create
:feature_category: :authentication_and_authorization
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
+- :name: authorized_project_update:authorized_project_update_user_refresh_over_user_range
+ :feature_category: :authentication_and_authorization
:has_external_dependencies:
:urgency: :low
:resource_boundary: :unknown
@@ -107,6 +115,14 @@
:weight: 1
:idempotent:
:tags: []
+- :name: cronjob:authorized_project_update_periodic_recalculate
+ :feature_category: :source_code_management
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent: true
+ :tags: []
- :name: cronjob:ci_archive_traces_cron
:feature_category: :continuous_integration
:has_external_dependencies:
diff --git a/app/workers/authorized_project_update/periodic_recalculate_worker.rb b/app/workers/authorized_project_update/periodic_recalculate_worker.rb
new file mode 100644
index 00000000000..0d1ad67d7bb
--- /dev/null
+++ b/app/workers/authorized_project_update/periodic_recalculate_worker.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module AuthorizedProjectUpdate
+ class PeriodicRecalculateWorker
+ include ApplicationWorker
+ # This worker does not perform work scoped to a context
+ include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
+
+ feature_category :source_code_management
+ urgency :low
+
+ idempotent!
+
+ def perform
+ if ::Feature.enabled?(:periodic_project_authorization_recalculation, default_enabled: true)
+ AuthorizedProjectUpdate::PeriodicRecalculateService.new.execute
+ end
+ end
+ end
+end
diff --git a/app/workers/authorized_project_update/user_refresh_over_user_range_worker.rb b/app/workers/authorized_project_update/user_refresh_over_user_range_worker.rb
new file mode 100644
index 00000000000..336b1c5443e
--- /dev/null
+++ b/app/workers/authorized_project_update/user_refresh_over_user_range_worker.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module AuthorizedProjectUpdate
+ class UserRefreshOverUserRangeWorker
+ include ApplicationWorker
+
+ feature_category :authentication_and_authorization
+ urgency :low
+ queue_namespace :authorized_project_update
+ deduplicate :until_executing, including_scheduled: true
+
+ idempotent!
+
+ def perform(start_user_id, end_user_id)
+ if ::Feature.enabled?(:periodic_project_authorization_recalculation, default_enabled: true)
+ AuthorizedProjectUpdate::RecalculateForUserRangeService.new(start_user_id, end_user_id).execute
+ end
+ end
+ end
+end