summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/badges/components/badge_settings.vue4
-rw-r--r--app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js37
-rw-r--r--app/assets/javascripts/blob/template_selector.js1
-rw-r--r--app/assets/javascripts/boards/index.js1
-rw-r--r--app/assets/javascripts/commons/vue.js1
-rw-r--r--app/assets/javascripts/create_item_dropdown.js1
-rw-r--r--app/assets/javascripts/diff_notes/services/resolve.js1
-rw-r--r--app/assets/javascripts/environments/components/stop_environment_modal.vue4
-rw-r--r--app/assets/javascripts/event_tracking/issue_sidebar.js2
-rw-r--r--app/assets/javascripts/event_tracking/notes.js2
-rw-r--r--app/assets/javascripts/filterable_list.js1
-rw-r--r--app/assets/javascripts/header.js5
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list.vue4
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue4
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue4
-rw-r--r--app/assets/javascripts/image_diff/helpers/badge_helper.js4
-rw-r--r--app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js4
-rw-r--r--app/assets/javascripts/import_projects/components/import_projects_table.vue45
-rw-r--r--app/assets/javascripts/import_projects/index.js2
-rw-r--r--app/assets/javascripts/import_projects/store/actions.js22
-rw-r--r--app/assets/javascripts/import_projects/store/getters.js5
-rw-r--r--app/assets/javascripts/import_projects/store/mutation_types.js2
-rw-r--r--app/assets/javascripts/import_projects/store/mutations.js4
-rw-r--r--app/assets/javascripts/import_projects/store/state.js1
-rw-r--r--app/assets/javascripts/integrations/integration_settings_form.js1
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue36
-rw-r--r--app/assets/javascripts/issue_show/index.js5
-rw-r--r--app/assets/javascripts/issue_show/services/index.js9
-rw-r--r--app/assets/javascripts/issue_show/stores/index.js1
-rw-r--r--app/assets/javascripts/jobs/components/log/collapsible_section.vue55
-rw-r--r--app/assets/javascripts/jobs/components/log/log.vue29
-rw-r--r--app/assets/javascripts/lib/utils/axios_utils.js8
-rw-r--r--app/assets/javascripts/main.js3
-rw-r--r--app/assets/javascripts/namespace_select.js1
-rw-r--r--app/assets/javascripts/notes.js1
-rw-r--r--app/assets/javascripts/notes/components/note_actions/reply_button.vue4
-rw-r--r--app/assets/javascripts/notes/index.js4
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue4
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js6
-rw-r--r--app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue4
-rw-r--r--app/assets/javascripts/pages/projects/commit/pipelines/index.js1
-rw-r--r--app/assets/javascripts/pages/projects/commit/show/index.js1
-rw-r--r--app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue4
-rw-r--r--app/assets/javascripts/pages/search/show/search.js1
-rw-r--r--app/assets/javascripts/performance_bar/components/detailed_metric.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_stop_modal.vue4
-rw-r--r--app/assets/javascripts/privacy_policy_update_callout.js2
-rw-r--r--app/assets/javascripts/profile/account/components/update_username.vue4
-rw-r--r--app/assets/javascripts/project_find_file.js2
-rw-r--r--app/assets/javascripts/ref_select_dropdown.js1
-rw-r--r--app/assets/javascripts/reports/components/modal.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignee_title.vue10
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue11
-rw-r--r--app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue11
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue8
-rw-r--r--app/assets/javascripts/sidebar/lib/sidebar_move_issue.js1
-rw-r--r--app/assets/javascripts/tracking.js103
-rw-r--r--app/assets/javascripts/vue_shared/components/deprecated_modal_2.vue118
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_modal.vue117
-rw-r--r--app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue2
-rw-r--r--app/assets/javascripts/vue_shared/vue_resource_interceptor.js34
-rw-r--r--app/assets/stylesheets/framework.scss1
-rw-r--r--app/assets/stylesheets/framework/contextual_sidebar.scss2
-rw-r--r--app/assets/stylesheets/framework/flash.scss2
-rw-r--r--app/assets/stylesheets/framework/selects.scss2
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss4
-rw-r--r--app/assets/stylesheets/pages/projects.scss7
-rw-r--r--app/controllers/dashboard/todos_controller.rb4
-rw-r--r--app/controllers/import/github_controller.rb21
-rw-r--r--app/controllers/projects/artifacts_controller.rb38
-rw-r--r--app/controllers/projects/registry/tags_controller.rb35
-rw-r--r--app/controllers/projects/templates_controller.rb8
-rw-r--r--app/finders/artifacts_finder.rb24
-rw-r--r--app/helpers/issuables_helper.rb8
-rw-r--r--app/helpers/search_helper.rb19
-rw-r--r--app/helpers/sorting_helper.rb20
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/models/ci/job_artifact.rb6
-rw-r--r--app/models/clusters/cluster.rb2
-rw-r--r--app/models/lfs_object.rb1
-rw-r--r--app/models/project.rb1
-rw-r--r--app/models/project_services/jira_service.rb2
-rw-r--r--app/models/release.rb1
-rw-r--r--app/models/repository.rb28
-rw-r--r--app/presenters/project_presenter.rb3
-rw-r--r--app/services/import_export_clean_up_service.rb14
-rw-r--r--app/services/projects/container_repository/cleanup_tags_service.rb2
-rw-r--r--app/services/projects/container_repository/delete_tags_service.rb66
-rw-r--r--app/views/admin/projects/index.html.haml32
-rw-r--r--app/views/ci/runner/_how_to_setup_runner_automatically.html.haml22
-rw-r--r--app/views/clusters/clusters/show.html.haml9
-rw-r--r--app/views/groups/projects.html.haml41
-rw-r--r--app/views/groups/runners/_group_runners.html.haml4
-rw-r--r--app/views/layouts/_page.html.haml2
-rw-r--r--app/views/projects/issues/_issue.html.haml5
-rw-r--r--app/views/projects/mirrors/_mirror_repos.html.haml6
-rw-r--r--app/views/projects/mirrors/_ssh_host_keys.html.haml4
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml25
-rw-r--r--app/views/projects/settings/ci_cd/_autodevops_form.html.haml4
-rw-r--r--app/views/search/results/_empty.html.haml3
-rw-r--r--app/views/shared/_remote_mirror_update_button.html.haml2
102 files changed, 462 insertions, 798 deletions
diff --git a/app/assets/javascripts/badges/components/badge_settings.vue b/app/assets/javascripts/badges/components/badge_settings.vue
index 531f84ad272..75a522efe7e 100644
--- a/app/assets/javascripts/badges/components/badge_settings.vue
+++ b/app/assets/javascripts/badges/components/badge_settings.vue
@@ -2,7 +2,7 @@
import { mapState, mapActions } from 'vuex';
import createFlash from '~/flash';
import { s__ } from '~/locale';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import Badge from './badge.vue';
import BadgeForm from './badge_form.vue';
import BadgeList from './badge_list.vue';
@@ -13,7 +13,7 @@ export default {
Badge,
BadgeForm,
BadgeList,
- GlModal: DeprecatedModal2,
+ GlModal,
},
computed: {
...mapState(['badgeInModal', 'isEditing']),
diff --git a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js
index 87c8568802e..75777b910ca 100644
--- a/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js
+++ b/app/assets/javascripts/blob/balsamiq/balsamiq_viewer.js
@@ -1,7 +1,5 @@
import sqljs from 'sql.js';
import { template as _template } from 'underscore';
-import axios from '~/lib/utils/axios_utils';
-import { successCodes } from '~/lib/utils/http_status';
const PREVIEW_TEMPLATE = _template(`
<div class="card">
@@ -18,25 +16,30 @@ class BalsamiqViewer {
}
loadFile(endpoint) {
- return axios
- .get(endpoint, {
- responseType: 'arraybuffer',
- validateStatus(status) {
- return status !== successCodes.OK;
- },
- })
- .then(({ data }) => {
- this.renderFile(data);
- })
- .catch(e => {
- throw new Error(e);
- });
+ return new Promise((resolve, reject) => {
+ const xhr = new XMLHttpRequest();
+
+ xhr.open('GET', endpoint, true);
+ xhr.responseType = 'arraybuffer';
+ xhr.onload = loadEvent => this.fileLoaded(loadEvent, resolve, reject);
+ xhr.onerror = reject;
+
+ xhr.send();
+ });
+ }
+
+ fileLoaded(loadEvent, resolve, reject) {
+ if (loadEvent.target.status !== 200) return reject();
+
+ this.renderFile(loadEvent);
+
+ return resolve();
}
- renderFile(fileBuffer) {
+ renderFile(loadEvent) {
const container = document.createElement('ul');
- this.initDatabase(fileBuffer);
+ this.initDatabase(loadEvent.target.response);
const previews = this.getPreviews();
previews.forEach(preview => {
diff --git a/app/assets/javascripts/blob/template_selector.js b/app/assets/javascripts/blob/template_selector.js
index 02216e4e93d..9e69c7d7164 100644
--- a/app/assets/javascripts/blob/template_selector.js
+++ b/app/assets/javascripts/blob/template_selector.js
@@ -1,7 +1,6 @@
/* eslint-disable class-methods-use-this */
import $ from 'jquery';
-import '~/gl_dropdown';
export default class TemplateSelector {
constructor({ dropdown, data, pattern, wrapper, editor, $input } = {}) {
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index da2669e7cde..3bded4a3258 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -22,6 +22,7 @@ import Board from 'ee_else_ce/boards/components/board';
import BoardSidebar from 'ee_else_ce/boards/components/board_sidebar';
import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown';
import BoardAddIssuesModal from '~/boards/components/modal/index.vue';
+import '~/vue_shared/vue_resource_interceptor';
import {
NavigationType,
convertObjectPropsToCamelCase,
diff --git a/app/assets/javascripts/commons/vue.js b/app/assets/javascripts/commons/vue.js
index 8b62d78c043..798623b94fb 100644
--- a/app/assets/javascripts/commons/vue.js
+++ b/app/assets/javascripts/commons/vue.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import '../vue_shared/vue_resource_interceptor';
if (process.env.NODE_ENV !== 'production') {
Vue.config.productionTip = false;
diff --git a/app/assets/javascripts/create_item_dropdown.js b/app/assets/javascripts/create_item_dropdown.js
index 95b890b04c1..fa0f04c7d82 100644
--- a/app/assets/javascripts/create_item_dropdown.js
+++ b/app/assets/javascripts/create_item_dropdown.js
@@ -1,5 +1,4 @@
import _ from 'underscore';
-import '~/gl_dropdown';
export default class CreateItemDropdown {
/**
diff --git a/app/assets/javascripts/diff_notes/services/resolve.js b/app/assets/javascripts/diff_notes/services/resolve.js
index 27990b0a45e..0687028ca54 100644
--- a/app/assets/javascripts/diff_notes/services/resolve.js
+++ b/app/assets/javascripts/diff_notes/services/resolve.js
@@ -2,6 +2,7 @@
import Vue from 'vue';
import Flash from '../../flash';
+import '../../vue_shared/vue_resource_interceptor';
import { __ } from '~/locale';
window.gl = window.gl || {};
diff --git a/app/assets/javascripts/environments/components/stop_environment_modal.vue b/app/assets/javascripts/environments/components/stop_environment_modal.vue
index 1ea4e30a7c1..2cc3412e075 100644
--- a/app/assets/javascripts/environments/components/stop_environment_modal.vue
+++ b/app/assets/javascripts/environments/components/stop_environment_modal.vue
@@ -1,7 +1,7 @@
<script>
/* eslint-disable @gitlab/vue-i18n/no-bare-strings */
import { GlTooltipDirective } from '@gitlab/ui';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import eventHub from '../event_hub';
@@ -11,7 +11,7 @@ export default {
name: 'StopEnvironmentModal',
components: {
- GlModal: DeprecatedModal2,
+ GlModal,
LoadingButton,
},
diff --git a/app/assets/javascripts/event_tracking/issue_sidebar.js b/app/assets/javascripts/event_tracking/issue_sidebar.js
new file mode 100644
index 00000000000..6909f82c66f
--- /dev/null
+++ b/app/assets/javascripts/event_tracking/issue_sidebar.js
@@ -0,0 +1,2 @@
+export const initSidebarTracking = () => {};
+export const trackEvent = () => {};
diff --git a/app/assets/javascripts/event_tracking/notes.js b/app/assets/javascripts/event_tracking/notes.js
new file mode 100644
index 00000000000..1f70290c397
--- /dev/null
+++ b/app/assets/javascripts/event_tracking/notes.js
@@ -0,0 +1,2 @@
+// Noop function which has a EE counter-part
+export default () => {};
diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js
index c21fba06d42..77080691dcb 100644
--- a/app/assets/javascripts/filterable_list.js
+++ b/app/assets/javascripts/filterable_list.js
@@ -22,7 +22,6 @@ export default class FilterableList {
getPagePath() {
const action = this.filterForm.getAttribute('action');
- // eslint-disable-next-line no-jquery/no-serialize
const params = $(this.filterForm).serialize();
return `${action}${action.indexOf('?') > 0 ? '&' : '?'}${params}`;
}
diff --git a/app/assets/javascripts/header.js b/app/assets/javascripts/header.js
index fdd27e08793..3d846310008 100644
--- a/app/assets/javascripts/header.js
+++ b/app/assets/javascripts/header.js
@@ -15,10 +15,11 @@ import { parseBoolean } from '~/lib/utils/common_utils';
*/
export default function initTodoToggle() {
$(document).on('todo:toggle', (e, count) => {
+ const parsedCount = parseInt(count, 10);
const $todoPendingCount = $('.todos-count');
- $todoPendingCount.text(highCountTrim(count));
- $todoPendingCount.toggleClass('hidden', count === 0);
+ $todoPendingCount.text(highCountTrim(parsedCount));
+ $todoPendingCount.toggleClass('hidden', parsedCount === 0);
});
}
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
index e16918ae025..4f1260de0bc 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
@@ -3,7 +3,7 @@ import $ from 'jquery';
import { mapActions } from 'vuex';
import { __, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import ListItem from './list_item.vue';
@@ -11,7 +11,7 @@ export default {
components: {
Icon,
ListItem,
- GlModal: DeprecatedModal2,
+ GlModal,
},
directives: {
tooltip,
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
index c14b8a47841..09c9d135614 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
@@ -4,12 +4,12 @@ import { mapActions } from 'vuex';
import { sprintf, __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import tooltip from '~/vue_shared/directives/tooltip';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
export default {
components: {
Icon,
- GlModal: DeprecatedModal2,
+ GlModal,
},
directives: {
tooltip,
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index a2dd31aebd4..f67666f1fbf 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -3,12 +3,12 @@ import $ from 'jquery';
import flash from '~/flash';
import { __, sprintf, s__ } from '~/locale';
import { mapActions, mapState, mapGetters } from 'vuex';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import { modalTypes } from '../../constants';
export default {
components: {
- GlModal: DeprecatedModal2,
+ GlModal,
},
data() {
return {
diff --git a/app/assets/javascripts/image_diff/helpers/badge_helper.js b/app/assets/javascripts/image_diff/helpers/badge_helper.js
index 7921650e8a0..000157efad0 100644
--- a/app/assets/javascripts/image_diff/helpers/badge_helper.js
+++ b/app/assets/javascripts/image_diff/helpers/badge_helper.js
@@ -1,5 +1,3 @@
-import { spriteIcon } from '~/lib/utils/common_utils';
-
export function createImageBadge(noteId, { x, y }, classNames = []) {
const buttonEl = document.createElement('button');
const classList = classNames.concat(['js-image-badge']);
@@ -22,7 +20,7 @@ export function addImageBadge(containerEl, { coordinate, badgeText, noteId }) {
export function addImageCommentBadge(containerEl, { coordinate, noteId }) {
const buttonEl = createImageBadge(noteId, coordinate, ['image-comment-badge']);
- buttonEl.innerHTML = spriteIcon('image-comment-dark');
+ buttonEl.innerHTML = gl.utils.spriteIcon('image-comment-dark');
containerEl.appendChild(buttonEl);
}
diff --git a/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js b/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
index df3d90cff68..7051a968dac 100644
--- a/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
+++ b/app/assets/javascripts/image_diff/helpers/comment_indicator_helper.js
@@ -1,5 +1,3 @@
-import { spriteIcon } from '~/lib/utils/common_utils';
-
export function addCommentIndicator(containerEl, { x, y }) {
const buttonEl = document.createElement('button');
buttonEl.classList.add('btn-transparent');
@@ -8,7 +6,7 @@ export function addCommentIndicator(containerEl, { x, y }) {
buttonEl.style.left = `${x}px`;
buttonEl.style.top = `${y}px`;
- buttonEl.innerHTML = spriteIcon('image-comment-dark');
+ buttonEl.innerHTML = gl.utils.spriteIcon('image-comment-dark');
containerEl.appendChild(buttonEl);
}
diff --git a/app/assets/javascripts/import_projects/components/import_projects_table.vue b/app/assets/javascripts/import_projects/components/import_projects_table.vue
index e5ac3cbafe5..00eb0afb3bf 100644
--- a/app/assets/javascripts/import_projects/components/import_projects_table.vue
+++ b/app/assets/javascripts/import_projects/components/import_projects_table.vue
@@ -1,5 +1,4 @@
<script>
-import _ from 'underscore';
import { mapActions, mapState, mapGetters } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
@@ -8,8 +7,6 @@ import ImportedProjectTableRow from './imported_project_table_row.vue';
import ProviderRepoTableRow from './provider_repo_table_row.vue';
import eventHub from '../event_hub';
-const reposFetchThrottleDelay = 1000;
-
export default {
name: 'ImportProjectsTable',
components: {
@@ -26,11 +23,11 @@ export default {
},
computed: {
- ...mapState(['importedProjects', 'providerRepos', 'isLoadingRepos', 'filter']),
+ ...mapState(['importedProjects', 'providerRepos', 'isLoadingRepos']),
...mapGetters(['isImportingAnyRepo', 'hasProviderRepos', 'hasImportedProjects']),
emptyStateText() {
- return sprintf(__('No %{providerTitle} repositories found'), {
+ return sprintf(__('No %{providerTitle} repositories available to import'), {
providerTitle: this.providerTitle,
});
},
@@ -50,38 +47,21 @@ export default {
},
methods: {
- ...mapActions([
- 'fetchRepos',
- 'fetchReposFiltered',
- 'fetchJobs',
- 'stopJobsPolling',
- 'clearJobsEtagPoll',
- 'setFilter',
- ]),
+ ...mapActions(['fetchRepos', 'fetchJobs', 'stopJobsPolling', 'clearJobsEtagPoll']),
importAll() {
eventHub.$emit('importAll');
},
-
- handleFilterInput({ target }) {
- this.setFilter(target.value);
- },
-
- throttledFetchRepos: _.throttle(function fetch() {
- eventHub.$off('importAll');
- this.fetchRepos();
- }, reposFetchThrottleDelay),
},
};
</script>
<template>
<div>
- <p class="light text-nowrap mt-2">
- {{ s__('ImportProjects|Select the projects you want to import') }}
- </p>
-
<div class="d-flex justify-content-between align-items-end flex-wrap mb-3">
+ <p class="light text-nowrap mt-2 my-sm-0">
+ {{ s__('ImportProjects|Select the projects you want to import') }}
+ </p>
<loading-button
container-class="btn btn-success js-import-all"
:loading="isImportingAnyRepo"
@@ -90,19 +70,6 @@ export default {
type="button"
@click="importAll"
/>
- <form novalidate @submit.prevent>
- <input
- :value="filter"
- data-qa-selector="githubish_import_filter_field"
- class="form-control"
- name="filter"
- :placeholder="__('Filter your projects by name')"
- autofocus
- size="40"
- @input="handleFilterInput($event)"
- @keyup.enter="throttledFetchRepos"
- />
- </form>
</div>
<gl-loading-icon
v-if="isLoadingRepos"
diff --git a/app/assets/javascripts/import_projects/index.js b/app/assets/javascripts/import_projects/index.js
index b069dcb7766..2d99d716609 100644
--- a/app/assets/javascripts/import_projects/index.js
+++ b/app/assets/javascripts/import_projects/index.js
@@ -38,7 +38,7 @@ export default function mountImportProjectsTable(mountElement) {
},
methods: {
- ...mapActions(['setInitialData', 'setFilter']),
+ ...mapActions(['setInitialData']),
},
render(createElement) {
diff --git a/app/assets/javascripts/import_projects/store/actions.js b/app/assets/javascripts/import_projects/store/actions.js
index 0fb9a4cdfd4..c44500937cc 100644
--- a/app/assets/javascripts/import_projects/store/actions.js
+++ b/app/assets/javascripts/import_projects/store/actions.js
@@ -5,7 +5,6 @@ import Poll from '~/lib/utils/poll';
import createFlash from '~/flash';
import { s__, sprintf } from '~/locale';
import axios from '~/lib/utils/axios_utils';
-import { jobsPathWithFilter, reposPathWithFilter } from './getters';
let eTagPoll;
@@ -20,20 +19,16 @@ export const restartJobsPolling = () => {
};
export const setInitialData = ({ commit }, data) => commit(types.SET_INITIAL_DATA, data);
-export const setFilter = ({ commit }, filter) => commit(types.SET_FILTER, filter);
export const requestRepos = ({ commit }, repos) => commit(types.REQUEST_REPOS, repos);
export const receiveReposSuccess = ({ commit }, repos) =>
commit(types.RECEIVE_REPOS_SUCCESS, repos);
export const receiveReposError = ({ commit }) => commit(types.RECEIVE_REPOS_ERROR);
export const fetchRepos = ({ state, dispatch }) => {
- dispatch('stopJobsPolling');
dispatch('requestRepos');
- const { provider } = state;
-
return axios
- .get(reposPathWithFilter(state))
+ .get(state.reposPath)
.then(({ data }) =>
dispatch('receiveReposSuccess', convertObjectPropsToCamelCase(data, { deep: true })),
)
@@ -41,7 +36,7 @@ export const fetchRepos = ({ state, dispatch }) => {
.catch(() => {
createFlash(
sprintf(s__('ImportProjects|Requesting your %{provider} repositories failed'), {
- provider,
+ provider: state.provider,
}),
);
@@ -82,23 +77,16 @@ export const fetchImport = ({ state, dispatch }, { newName, targetNamespace, rep
export const receiveJobsSuccess = ({ commit }, updatedProjects) =>
commit(types.RECEIVE_JOBS_SUCCESS, updatedProjects);
export const fetchJobs = ({ state, dispatch }) => {
- const { filter } = state;
-
- if (eTagPoll) {
- stopJobsPolling();
- clearJobsEtagPoll();
- }
+ if (eTagPoll) return;
eTagPoll = new Poll({
resource: {
- fetchJobs: () => axios.get(jobsPathWithFilter(state)),
+ fetchJobs: () => axios.get(state.jobsPath),
},
method: 'fetchJobs',
successCallback: ({ data }) =>
dispatch('receiveJobsSuccess', convertObjectPropsToCamelCase(data, { deep: true })),
- errorCallback: () =>
- createFlash(s__('ImportProjects|Update of imported projects with realtime changes failed')),
- data: { filter },
+ errorCallback: () => createFlash(s__('ImportProjects|Updating the imported projects failed')),
});
if (!Visibility.hidden()) {
diff --git a/app/assets/javascripts/import_projects/store/getters.js b/app/assets/javascripts/import_projects/store/getters.js
index b107c293181..727b80765bd 100644
--- a/app/assets/javascripts/import_projects/store/getters.js
+++ b/app/assets/javascripts/import_projects/store/getters.js
@@ -20,8 +20,3 @@ export const isImportingAnyRepo = state => state.reposBeingImported.length > 0;
export const hasProviderRepos = state => state.providerRepos.length > 0;
export const hasImportedProjects = state => state.importedProjects.length > 0;
-
-export const reposPathWithFilter = ({ reposPath, filter = '' }) =>
- filter ? `${reposPath}?filter=${filter}` : reposPath;
-export const jobsPathWithFilter = ({ jobsPath, filter = '' }) =>
- filter ? `${jobsPath}?filter=${filter}` : jobsPath;
diff --git a/app/assets/javascripts/import_projects/store/mutation_types.js b/app/assets/javascripts/import_projects/store/mutation_types.js
index 16574f4450f..6ba3fd6f29e 100644
--- a/app/assets/javascripts/import_projects/store/mutation_types.js
+++ b/app/assets/javascripts/import_projects/store/mutation_types.js
@@ -9,5 +9,3 @@ export const RECEIVE_IMPORT_SUCCESS = 'RECEIVE_IMPORT_SUCCESS';
export const RECEIVE_IMPORT_ERROR = 'RECEIVE_IMPORT_ERROR';
export const RECEIVE_JOBS_SUCCESS = 'RECEIVE_JOBS_SUCCESS';
-
-export const SET_FILTER = 'SET_FILTER';
diff --git a/app/assets/javascripts/import_projects/store/mutations.js b/app/assets/javascripts/import_projects/store/mutations.js
index 6c56cfa8298..b88de0268e7 100644
--- a/app/assets/javascripts/import_projects/store/mutations.js
+++ b/app/assets/javascripts/import_projects/store/mutations.js
@@ -6,10 +6,6 @@ export default {
Object.assign(state, data);
},
- [types.SET_FILTER](state, filter) {
- state.filter = filter;
- },
-
[types.REQUEST_REPOS](state) {
state.isLoadingRepos = true;
},
diff --git a/app/assets/javascripts/import_projects/store/state.js b/app/assets/javascripts/import_projects/store/state.js
index 829f3aa4fbb..637fef6e53c 100644
--- a/app/assets/javascripts/import_projects/store/state.js
+++ b/app/assets/javascripts/import_projects/store/state.js
@@ -12,5 +12,4 @@ export default () => ({
isLoadingRepos: false,
canSelectNamespace: false,
ciCdOnly: false,
- filter: '',
});
diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js
index 1c9b94ade8a..a7746bb3a0b 100644
--- a/app/assets/javascripts/integrations/integration_settings_form.js
+++ b/app/assets/javascripts/integrations/integration_settings_form.js
@@ -42,7 +42,6 @@ export default class IntegrationSettingsForm {
// and test the service using provided configuration.
if (this.$form.get(0).checkValidity() && this.canTestService) {
e.preventDefault();
- // eslint-disable-next-line no-jquery/no-serialize
this.testSettings(this.$form.serialize());
}
}
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index b8b3a4f44fd..88975c2cc73 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -102,10 +102,10 @@ export default {
required: false,
default: '',
},
- issuableTemplateNamesPath: {
- type: String,
+ issuableTemplates: {
+ type: Array,
required: false,
- default: '',
+ default: () => [],
},
markdownPreviewPath: {
type: String,
@@ -156,13 +156,9 @@ export default {
store,
state: store.state,
showForm: false,
- templatesRequested: false,
};
},
computed: {
- issuableTemplates() {
- return this.store.formState.issuableTemplates;
- },
formState() {
return this.store.formState;
},
@@ -237,7 +233,6 @@ export default {
}
return undefined;
},
-
updateStoreState() {
return this.service
.getData()
@@ -250,7 +245,7 @@ export default {
});
},
- updateAndShowForm(templates = []) {
+ openForm() {
if (!this.showForm) {
this.showForm = true;
this.store.setFormState({
@@ -259,32 +254,9 @@ export default {
lock_version: this.state.lock_version,
lockedWarningVisible: false,
updateLoading: false,
- issuableTemplates: templates,
- });
- }
- },
-
- requestTemplatesAndShowForm() {
- return this.service
- .loadTemplates(this.issuableTemplateNamesPath)
- .then(res => {
- this.updateAndShowForm(res.data);
- })
- .catch(() => {
- createFlash(this.defaultErrorMessage);
- this.updateAndShowForm();
});
- },
-
- openForm() {
- if (!this.templatesRequested) {
- this.templatesRequested = true;
- this.requestTemplatesAndShowForm();
- } else {
- this.updateAndShowForm(this.issuableTemplates);
}
},
-
closeForm() {
this.showForm = false;
},
diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js
index e170d338408..5a9dd91817e 100644
--- a/app/assets/javascripts/issue_show/index.js
+++ b/app/assets/javascripts/issue_show/index.js
@@ -1,6 +1,8 @@
import Vue from 'vue';
+import { initSidebarTracking } from 'ee_else_ce/event_tracking/issue_sidebar';
import issuableApp from './components/app.vue';
import { parseIssuableData } from './utils/parse_data';
+import '../vue_shared/vue_resource_interceptor';
export default function initIssueableApp() {
return new Vue({
@@ -8,6 +10,9 @@ export default function initIssueableApp() {
components: {
issuableApp,
},
+ mounted() {
+ initSidebarTracking();
+ },
render(createElement) {
return createElement('issuable-app', {
props: parseIssuableData(),
diff --git a/app/assets/javascripts/issue_show/services/index.js b/app/assets/javascripts/issue_show/services/index.js
index b1deeaae0fc..3c8334bee50 100644
--- a/app/assets/javascripts/issue_show/services/index.js
+++ b/app/assets/javascripts/issue_show/services/index.js
@@ -17,13 +17,4 @@ export default class Service {
updateIssuable(data) {
return axios.put(this.endpoint, data);
}
-
- // eslint-disable-next-line class-methods-use-this
- loadTemplates(templateNamesEndpoint) {
- if (!templateNamesEndpoint) {
- return Promise.resolve([]);
- }
-
- return axios.get(templateNamesEndpoint);
- }
}
diff --git a/app/assets/javascripts/issue_show/stores/index.js b/app/assets/javascripts/issue_show/stores/index.js
index d32747b5053..3c17e73ccec 100644
--- a/app/assets/javascripts/issue_show/stores/index.js
+++ b/app/assets/javascripts/issue_show/stores/index.js
@@ -9,7 +9,6 @@ export default class Store {
lockedWarningVisible: false,
updateLoading: false,
lock_version: 0,
- issuableTemplates: [],
};
}
diff --git a/app/assets/javascripts/jobs/components/log/collapsible_section.vue b/app/assets/javascripts/jobs/components/log/collapsible_section.vue
deleted file mode 100644
index 7c0deb08488..00000000000
--- a/app/assets/javascripts/jobs/components/log/collapsible_section.vue
+++ /dev/null
@@ -1,55 +0,0 @@
-<script>
-import LogLine from './line.vue';
-import LogLineHeader from './line_header.vue';
-
-export default {
- name: 'CollpasibleLogSection',
- components: {
- LogLine,
- LogLineHeader,
- },
- props: {
- section: {
- type: Object,
- required: true,
- },
- traceEndpoint: {
- type: String,
- required: true,
- },
- },
- computed: {
- badgeDuration() {
- return this.section.line && this.section.line.section_duration;
- },
- },
- methods: {
- handleOnClickCollapsibleLine(section) {
- this.$emit('onClickCollapsibleLine', section);
- },
- },
-};
-</script>
-<template>
- <div>
- <log-line-header
- :line="section.line"
- :duration="badgeDuration"
- :path="traceEndpoint"
- :is-closed="section.isClosed"
- @toggleLine="handleOnClickCollapsibleLine(section)"
- />
- <template v-if="!section.isClosed">
- <template v-for="line in section.lines">
- <collpasible-log-section
- v-if="line.isHeader"
- :key="`collapsible-nested-${line.offset}`"
- :section="line"
- :trace-endpoint="traceEndpoint"
- @toggleLine="handleOnClickCollapsibleLine"
- />
- <log-line v-else :key="line.offset" :line="line" :path="traceEndpoint" />
- </template>
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/jobs/components/log/log.vue b/app/assets/javascripts/jobs/components/log/log.vue
index ef126166e8b..429796aeb4e 100644
--- a/app/assets/javascripts/jobs/components/log/log.vue
+++ b/app/assets/javascripts/jobs/components/log/log.vue
@@ -1,12 +1,12 @@
<script>
import { mapState, mapActions } from 'vuex';
-import CollpasibleLogSection from './collapsible_section.vue';
import LogLine from './line.vue';
+import LogLineHeader from './line_header.vue';
export default {
components: {
- CollpasibleLogSection,
LogLine,
+ LogLineHeader,
},
computed: {
...mapState(['traceEndpoint', 'trace', 'isTraceComplete']),
@@ -22,13 +22,24 @@ export default {
<template>
<code class="job-log d-block">
<template v-for="(section, index) in trace">
- <collpasible-log-section
- v-if="section.isHeader"
- :key="`collapsible-${index}`"
- :section="section"
- :trace-endpoint="traceEndpoint"
- @onClickCollapsibleLine="handleOnClickCollapsibleLine"
- />
+ <template v-if="section.isHeader">
+ <log-line-header
+ :key="`collapsible-${index}`"
+ :line="section.line"
+ :duration="section.section_duration"
+ :path="traceEndpoint"
+ :is-closed="section.isClosed"
+ @toggleLine="handleOnClickCollapsibleLine(section)"
+ />
+ <template v-if="!section.isClosed">
+ <log-line
+ v-for="line in section.lines"
+ :key="line.offset"
+ :line="line"
+ :path="traceEndpoint"
+ />
+ </template>
+ </template>
<log-line v-else :key="section.offset" :line="section" :path="traceEndpoint" />
</template>
diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js
index c17f62c671c..37721cd030c 100644
--- a/app/assets/javascripts/lib/utils/axios_utils.js
+++ b/app/assets/javascripts/lib/utils/axios_utils.js
@@ -8,19 +8,19 @@ axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
// Maintain a global counter for active requests
// see: spec/support/wait_for_requests.rb
axios.interceptors.request.use(config => {
- window.pendingRequests = window.pendingRequests || 0;
- window.pendingRequests += 1;
+ window.activeVueResources = window.activeVueResources || 0;
+ window.activeVueResources += 1;
return config;
});
// Remove the global counter
axios.interceptors.response.use(
response => {
- window.pendingRequests -= 1;
+ window.activeVueResources -= 1;
return response;
},
err => {
- window.pendingRequests -= 1;
+ window.activeVueResources -= 1;
return Promise.reject(err);
},
);
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index c19a845eb69..0ddf40b0405 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -37,7 +37,6 @@ import GlFieldErrors from './gl_field_errors';
import initUserPopovers from './user_popovers';
import { initUserTracking } from './tracking';
import { __ } from './locale';
-import initPrivacyPolicyUpdateCallout from './privacy_policy_update_callout';
import 'ee_else_ce/main_ee';
@@ -97,7 +96,6 @@ function deferredInitialisation() {
initUsagePingConsent();
initUserPopovers();
initUserTracking();
- initPrivacyPolicyUpdateCallout();
if (document.querySelector('.search')) initSearchAutocomplete();
@@ -314,7 +312,6 @@ document.addEventListener('DOMContentLoaded', () => {
const action = `${this.action}${link.search === '' ? '?' : '&'}`;
event.preventDefault();
- // eslint-disable-next-line no-jquery/no-serialize
visitUrl(`${action}${$(this).serialize()}`);
});
diff --git a/app/assets/javascripts/namespace_select.js b/app/assets/javascripts/namespace_select.js
index 4660e4397a2..4ddbec71ba6 100644
--- a/app/assets/javascripts/namespace_select.js
+++ b/app/assets/javascripts/namespace_select.js
@@ -1,7 +1,6 @@
/* eslint-disable func-names, no-else-return, prefer-template, prefer-arrow-callback */
import $ from 'jquery';
-import '~/gl_dropdown';
import Api from './api';
import { mergeUrlParams } from './lib/utils/url_utility';
import { parseBoolean } from '~/lib/utils/common_utils';
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index ed52eec8b18..9cc56b34c75 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1461,7 +1461,6 @@ export default class Notes {
getFormData($form) {
const content = $form.find('.js-note-text').val();
return {
- // eslint-disable-next-line no-jquery/no-serialize
formData: $form.serialize(),
formContent: _.escape(content),
formAction: $form.attr('action'),
diff --git a/app/assets/javascripts/notes/components/note_actions/reply_button.vue b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
index 8bdee759a23..1aeb07d6608 100644
--- a/app/assets/javascripts/notes/components/note_actions/reply_button.vue
+++ b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
@@ -19,9 +19,7 @@ export default {
<gl-button
ref="button"
v-gl-tooltip
- class="note-action-button"
- data-track-event="click_button"
- data-track-label="reply_comment_button"
+ class="note-action-button js-note-action-reply"
variant="transparent"
:title="__('Reply to comment')"
@click="$emit('startReplying')"
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index 30372103590..c70c0e4095c 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import initNoteStats from 'ee_else_ce/event_tracking/notes';
import notesApp from './components/notes_app.vue';
import initDiscussionFilters from './discussion_filters';
import createStore from './stores';
@@ -38,6 +39,9 @@ document.addEventListener('DOMContentLoaded', () => {
notesData: JSON.parse(notesDataset.notesData),
};
},
+ mounted() {
+ initNoteStats();
+ },
render(createElement) {
return createElement('notes-app', {
props: {
diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
index eb03baf4894..e2fec3c7172 100644
--- a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
+++ b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
@@ -1,13 +1,13 @@
<script>
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
export default {
components: {
- GlModal: DeprecatedModal2,
+ GlModal,
},
props: {
url: {
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index 5230bdf9cdd..d51d411f3c6 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -1,11 +1,9 @@
/* eslint-disable class-methods-use-this, no-unneeded-ternary */
import $ from 'jquery';
-import '~/gl_dropdown';
import { visitUrl } from '~/lib/utils/url_utility';
import UsersSelect from '~/users_select';
import { isMetaClick } from '~/lib/utils/common_utils';
-import { addDelimiter } from '~/lib/utils/text_utility';
import { __ } from '~/locale';
import flash from '~/flash';
import axios from '~/lib/utils/axios_utils';
@@ -147,8 +145,8 @@ export default class Todos {
updateBadges(data) {
$(document).trigger('todo:toggle', data.count);
- document.querySelector('.todos-pending .badge').innerHTML = addDelimiter(data.count);
- document.querySelector('.todos-done .badge').innerHTML = addDelimiter(data.done_count);
+ document.querySelector('.todos-pending .badge').innerHTML = data.count;
+ document.querySelector('.todos-done .badge').innerHTML = data.done_count;
}
goToTodoUrl(e) {
diff --git a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
index 26adf4cbbe0..c563514d36b 100644
--- a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
+++ b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
@@ -1,14 +1,14 @@
<script>
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
export default {
components: {
- GlModal: DeprecatedModal2,
+ GlModal,
},
props: {
milestoneTitle: {
diff --git a/app/assets/javascripts/pages/projects/commit/pipelines/index.js b/app/assets/javascripts/pages/projects/commit/pipelines/index.js
index 9f08260c3d6..8cc3cb0a57c 100644
--- a/app/assets/javascripts/pages/projects/commit/pipelines/index.js
+++ b/app/assets/javascripts/pages/projects/commit/pipelines/index.js
@@ -6,7 +6,6 @@ document.addEventListener('DOMContentLoaded', () => {
new MiniPipelineGraph({
container: '.js-commit-pipeline-graph',
}).bindEvents();
- // eslint-disable-next-line no-jquery/no-load
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
initPipelines();
});
diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js
index 5aa4734244e..6fc982967eb 100644
--- a/app/assets/javascripts/pages/projects/commit/show/index.js
+++ b/app/assets/javascripts/pages/projects/commit/show/index.js
@@ -21,7 +21,6 @@ document.addEventListener('DOMContentLoaded', () => {
}).bindEvents();
initNotes();
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight + performanceHeight);
- // eslint-disable-next-line no-jquery/no-load
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
fetchCommitMergeRequests();
initDiffNotes();
diff --git a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue
index bb95f33c838..e723cd3fea9 100644
--- a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue
+++ b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue
@@ -2,14 +2,14 @@
import _ from 'underscore';
import axios from '~/lib/utils/axios_utils';
import createFlash from '~/flash';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility';
import eventHub from '../event_hub';
export default {
components: {
- GlModal: DeprecatedModal2,
+ GlModal,
},
props: {
url: {
diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js
index dff9d855b67..8f6c48ab065 100644
--- a/app/assets/javascripts/pages/search/show/search.js
+++ b/app/assets/javascripts/pages/search/show/search.js
@@ -1,5 +1,4 @@
import $ from 'jquery';
-import '~/gl_dropdown';
import Flash from '~/flash';
import Api from '~/api';
import { __ } from '~/locale';
diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
index f2d98cf07e1..a271284dd89 100644
--- a/app/assets/javascripts/performance_bar/components/detailed_metric.vue
+++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
@@ -1,10 +1,10 @@
<script>
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import Icon from '~/vue_shared/components/icon.vue';
export default {
components: {
- GlModal: DeprecatedModal2,
+ GlModal,
Icon,
},
props: {
diff --git a/app/assets/javascripts/pipelines/components/pipeline_stop_modal.vue b/app/assets/javascripts/pipelines/components/pipeline_stop_modal.vue
index 2e71b3c637b..4cafd147511 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_stop_modal.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_stop_modal.vue
@@ -1,6 +1,6 @@
<script>
import _ from 'underscore';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import { GlLink } from '@gitlab/ui';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
@@ -13,7 +13,7 @@ import { s__, sprintf } from '~/locale';
*/
export default {
components: {
- GlModal: DeprecatedModal2,
+ GlModal,
GlLink,
ClipboardButton,
CiIcon,
diff --git a/app/assets/javascripts/privacy_policy_update_callout.js b/app/assets/javascripts/privacy_policy_update_callout.js
index 97f41deb30f..126b1ee1132 100644
--- a/app/assets/javascripts/privacy_policy_update_callout.js
+++ b/app/assets/javascripts/privacy_policy_update_callout.js
@@ -1,7 +1,7 @@
import PersistentUserCallout from '~/persistent_user_callout';
function initPrivacyPolicyUpdateCallout() {
- const callout = document.querySelector('.js-privacy-policy-update');
+ const callout = document.querySelector('.privacy-policy-update-64341');
PersistentUserCallout.factory(callout);
}
diff --git a/app/assets/javascripts/profile/account/components/update_username.vue b/app/assets/javascripts/profile/account/components/update_username.vue
index 72867ecd709..e1085c0a44d 100644
--- a/app/assets/javascripts/profile/account/components/update_username.vue
+++ b/app/assets/javascripts/profile/account/components/update_username.vue
@@ -1,13 +1,13 @@
<script>
import _ from 'underscore';
import axios from '~/lib/utils/axios_utils';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
import { s__, sprintf } from '~/locale';
import Flash from '~/flash';
export default {
components: {
- GlModal: DeprecatedModal2,
+ GlModal,
},
props: {
actionUrl: {
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index c198c4eea4a..e73a828c0ae 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -81,7 +81,7 @@ export default class ProjectFindFile {
// find file
}
- // files paths load
+ // files pathes load
load(url) {
axios
.get(url)
diff --git a/app/assets/javascripts/ref_select_dropdown.js b/app/assets/javascripts/ref_select_dropdown.js
index 2e0113271df..75bac035aca 100644
--- a/app/assets/javascripts/ref_select_dropdown.js
+++ b/app/assets/javascripts/ref_select_dropdown.js
@@ -1,5 +1,4 @@
import $ from 'jquery';
-import '~/gl_dropdown';
class RefSelectDropdown {
constructor($dropdownButton, availableRefs) {
diff --git a/app/assets/javascripts/reports/components/modal.vue b/app/assets/javascripts/reports/components/modal.vue
index 6019af2dfe0..cb9c1642608 100644
--- a/app/assets/javascripts/reports/components/modal.vue
+++ b/app/assets/javascripts/reports/components/modal.vue
@@ -1,13 +1,13 @@
<script>
// import { sprintf, __ } from '~/locale';
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import Modal from '~/vue_shared/components/gl_modal.vue';
import LoadingButton from '~/vue_shared/components/loading_button.vue';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import { fieldTypes } from '../constants';
export default {
components: {
- Modal: DeprecatedModal2,
+ Modal,
LoadingButton,
CodeBlock,
},
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
index f4dac38b9e1..63b93a80ead 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignee_title.vue
@@ -1,5 +1,6 @@
<script>
import { n__ } from '~/locale';
+import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar';
export default {
name: 'AssigneeTitle',
@@ -29,6 +30,11 @@ export default {
return n__('Assignee', `%d Assignees`, assignees);
},
},
+ methods: {
+ trackEdit() {
+ trackEvent('click_edit_button', 'assignee');
+ },
+ },
};
</script>
<template>
@@ -39,9 +45,7 @@ export default {
v-if="editable"
class="js-sidebar-dropdown-toggle edit-link float-right"
href="#"
- data-track-event="click_edit_button"
- data-track-label="right_sidebar"
- data-track-property="assignee"
+ @click.prevent="trackEdit"
>
{{ __('Edit') }}
</a>
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
index e350264de96..1c75b6148e8 100644
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
@@ -5,6 +5,7 @@ import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '~/sidebar/event_hub';
import editForm from './edit_form.vue';
+import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar';
export default {
components: {
@@ -51,6 +52,11 @@ export default {
toggleForm() {
this.edit = !this.edit;
},
+ onEditClick() {
+ this.toggleForm();
+
+ trackEvent('click_edit_button', 'confidentiality');
+ },
updateConfidentialAttribute(confidential) {
this.service
.update('issue', { confidential })
@@ -82,10 +88,7 @@ export default {
v-if="isEditable"
class="float-right confidential-edit"
href="#"
- data-track-event="click_edit_button"
- data-track-label="right_sidebar"
- data-track-property="confidentiality"
- @click.prevent="toggleForm"
+ @click.prevent="onEditClick"
>
{{ __('Edit') }}
</a>
diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
index c7c5e0e20f1..ec2a7b93a98 100644
--- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
@@ -6,6 +6,7 @@ import issuableMixin from '~/vue_shared/mixins/issuable';
import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '~/sidebar/event_hub';
import editForm from './edit_form.vue';
+import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar';
export default {
components: {
@@ -65,6 +66,11 @@ export default {
toggleForm() {
this.mediator.store.isLockDialogOpen = !this.mediator.store.isLockDialogOpen;
},
+ onEditClick() {
+ this.toggleForm();
+
+ trackEvent('click_edit_button', 'lock_issue');
+ },
updateLockedAttribute(locked) {
this.mediator.service
.update(this.issuableType, {
@@ -108,10 +114,7 @@ export default {
v-if="isEditable"
class="float-right lock-edit"
type="button"
- data-track-event="click_edit_button"
- data-track-label="right_sidebar"
- data-track-property="lock_issue"
- @click.prevent="toggleForm"
+ @click.prevent="onEditClick"
>
{{ __('Edit') }}
</button>
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
index ea5edb3ce3f..1f5f19d1931 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
@@ -1,10 +1,10 @@
<script>
import { __ } from '~/locale';
-import Tracking from '~/tracking';
import icon from '~/vue_shared/components/icon.vue';
import toggleButton from '~/vue_shared/components/toggle_button.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import eventHub from '../../event_hub';
+import { trackEvent } from 'ee_else_ce/event_tracking/issue_sidebar';
const ICON_ON = 'notifications';
const ICON_OFF = 'notifications-off';
@@ -19,7 +19,6 @@ export default {
icon,
toggleButton,
},
- mixins: [Tracking.mixin({ label: 'right_sidebar' })],
props: {
loading: {
type: Boolean,
@@ -66,10 +65,7 @@ export default {
// Component event emission.
this.$emit('toggleSubscription', this.id);
- this.track('toggle_button', {
- property: 'notifications',
- value: this.subscribed ? 0 : 1,
- });
+ trackEvent('toggle_button', 'notifications', this.subscribed ? 0 : 1);
},
onClickCollapsedIcon() {
this.$emit('toggleSidebar');
diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
index 66d1fed7d31..110175a6779 100644
--- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
+++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
@@ -1,5 +1,4 @@
import $ from 'jquery';
-import '~/gl_dropdown';
import _ from 'underscore';
import { __ } from '~/locale';
diff --git a/app/assets/javascripts/tracking.js b/app/assets/javascripts/tracking.js
index 7c0097fbe37..1b4ca1d5741 100644
--- a/app/assets/javascripts/tracking.js
+++ b/app/assets/javascripts/tracking.js
@@ -1,4 +1,4 @@
-import _ from 'underscore';
+import $ from 'jquery';
const DEFAULT_SNOWPLOW_OPTIONS = {
namespace: 'gl',
@@ -14,31 +14,18 @@ const DEFAULT_SNOWPLOW_OPTIONS = {
linkClickTracking: false,
};
-const eventHandler = (e, func, opts = {}) => {
- const el = e.target.closest('[data-track-event]');
- const action = el && el.dataset.trackEvent;
- if (!action) return;
-
- let value = el.dataset.trackValue || el.value || undefined;
- if (el.type === 'checkbox' && !el.checked) value = false;
-
- const data = {
- label: el.dataset.trackLabel,
- property: el.dataset.trackProperty,
- value,
- context: el.dataset.trackContext,
- };
-
- func(opts.category, action + (opts.suffix || ''), _.omit(data, _.isUndefined));
-};
-
-const eventHandlers = (category, func) => {
- const handler = opts => e => eventHandler(e, func, { ...{ category }, ...opts });
- const handlers = [];
- handlers.push({ name: 'click', func: handler() });
- handlers.push({ name: 'show.bs.dropdown', func: handler({ suffix: '_show' }) });
- handlers.push({ name: 'hide.bs.dropdown', func: handler({ suffix: '_hide' }) });
- return handlers;
+const extractData = (el, opts = {}) => {
+ const { trackEvent, trackLabel = '', trackProperty = '' } = el.dataset;
+ let trackValue = el.dataset.trackValue || el.value || '';
+ if (el.type === 'checkbox' && !el.checked) trackValue = false;
+ return [
+ trackEvent + (opts.suffix || ''),
+ {
+ label: trackLabel,
+ property: trackProperty,
+ value: trackValue,
+ },
+ ];
};
export default class Tracking {
@@ -52,43 +39,49 @@ export default class Tracking {
return typeof window.snowplow === 'function' && this.trackable();
}
- static event(category = document.body.dataset.page, action = 'generic', data = {}) {
+ static event(category = document.body.dataset.page, event = 'generic', data = {}) {
if (!this.enabled()) return false;
// eslint-disable-next-line @gitlab/i18n/no-non-i18n-strings
if (!category) throw new Error('Tracking: no category provided for tracking.');
- const { label, property, value, context } = data;
- const contexts = context ? [context] : undefined;
- return window.snowplow('trackStructEvent', category, action, label, property, value, contexts);
+ return window.snowplow(
+ 'trackStructEvent',
+ category,
+ event,
+ Object.assign({}, { label: '', property: '', value: '' }, data),
+ );
}
- static bindDocument(category = document.body.dataset.page, documentOverride = null) {
- const el = documentOverride || document;
- if (!this.enabled() || el.trackingBound) return [];
+ constructor(category = document.body.dataset.page) {
+ this.category = category;
+ }
+
+ bind(container = document) {
+ if (!this.constructor.enabled()) return;
+ container.querySelectorAll(`[data-track-event]`).forEach(el => {
+ if (this.customHandlingFor(el)) return;
+ // jquery is required for select2, so we use it always
+ // see: https://github.com/select2/select2/issues/4686
+ $(el).on('click', this.eventHandler(this.category));
+ });
+ }
- el.trackingBound = true;
+ customHandlingFor(el) {
+ const classes = el.classList;
- const handlers = eventHandlers(category, (...args) => this.event(...args));
- handlers.forEach(event => el.addEventListener(event.name, event.func));
- return handlers;
+ // bootstrap dropdowns
+ if (classes.contains('dropdown')) {
+ $(el).on('show.bs.dropdown', this.eventHandler(this.category, { suffix: '_show' }));
+ $(el).on('hide.bs.dropdown', this.eventHandler(this.category, { suffix: '_hide' }));
+ return true;
+ }
+
+ return false;
}
- static mixin(opts) {
- return {
- data() {
- return {
- tracking: {
- // eslint-disable-next-line no-underscore-dangle
- category: this.$options.name || this.$options._componentTag,
- },
- };
- },
- methods: {
- track(action, data) {
- const category = opts.category || data.category || this.tracking.category;
- Tracking.event(category || 'unspecified', action, { ...opts, ...this.tracking, ...data });
- },
- },
+ eventHandler(category = null, opts = {}) {
+ return e => {
+ this.constructor.event(category || this.category, ...extractData(e.currentTarget, opts));
};
}
}
@@ -96,7 +89,7 @@ export default class Tracking {
export function initUserTracking() {
if (!Tracking.enabled()) return;
- const opts = { ...DEFAULT_SNOWPLOW_OPTIONS, ...window.snowplowOptions };
+ const opts = Object.assign({}, DEFAULT_SNOWPLOW_OPTIONS, window.snowplowOptions);
window.snowplow('newTracker', opts.namespace, opts.hostname, opts);
window.snowplow('enableActivityTracking', 30, 30);
@@ -104,6 +97,4 @@ export function initUserTracking() {
if (opts.formTracking) window.snowplow('enableFormTracking');
if (opts.linkClickTracking) window.snowplow('enableLinkClickTracking');
-
- Tracking.bindDocument();
}
diff --git a/app/assets/javascripts/vue_shared/components/deprecated_modal_2.vue b/app/assets/javascripts/vue_shared/components/deprecated_modal_2.vue
deleted file mode 100644
index 543547b37fe..00000000000
--- a/app/assets/javascripts/vue_shared/components/deprecated_modal_2.vue
+++ /dev/null
@@ -1,118 +0,0 @@
-<script>
-import $ from 'jquery';
-
-const buttonVariants = ['danger', 'primary', 'success', 'warning'];
-const sizeVariants = ['sm', 'md', 'lg', 'xl'];
-
-export default {
- name: 'DeprecatedModal2', // use GlModal instead
-
- props: {
- id: {
- type: String,
- required: false,
- default: null,
- },
- modalSize: {
- type: String,
- required: false,
- default: 'md',
- validator: value => sizeVariants.includes(value),
- },
- headerTitleText: {
- type: String,
- required: false,
- default: '',
- },
- footerPrimaryButtonVariant: {
- type: String,
- required: false,
- default: 'primary',
- validator: value => buttonVariants.includes(value),
- },
- footerPrimaryButtonText: {
- type: String,
- required: false,
- default: '',
- },
- },
- computed: {
- modalSizeClass() {
- return this.modalSize === 'md' ? '' : `modal-${this.modalSize}`;
- },
- },
- mounted() {
- $(this.$el)
- .on('shown.bs.modal', this.opened)
- .on('hidden.bs.modal', this.closed);
- },
- beforeDestroy() {
- $(this.$el)
- .off('shown.bs.modal', this.opened)
- .off('hidden.bs.modal', this.closed);
- },
- methods: {
- emitCancel(event) {
- this.$emit('cancel', event);
- },
- emitSubmit(event) {
- this.$emit('submit', event);
- },
- opened() {
- this.$emit('open');
- },
- closed() {
- this.$emit('closed');
- },
- },
-};
-</script>
-
-<template>
- <div :id="id" class="modal fade" tabindex="-1" role="dialog">
- <div :class="modalSizeClass" class="modal-dialog" role="document">
- <div class="modal-content">
- <div class="modal-header">
- <slot name="header">
- <h4 class="modal-title">
- <slot name="title"> {{ headerTitleText }} </slot>
- </h4>
- <button
- :aria-label="s__('Modal|Close')"
- type="button"
- class="close js-modal-close-action"
- data-dismiss="modal"
- @click="emitCancel($event)"
- >
- <span aria-hidden="true">&times;</span>
- </button>
- </slot>
- </div>
-
- <div class="modal-body"><slot></slot></div>
-
- <div class="modal-footer">
- <slot name="footer">
- <button
- type="button"
- class="btn js-modal-cancel-action qa-modal-cancel-button"
- data-dismiss="modal"
- @click="emitCancel($event)"
- >
- {{ s__('Modal|Cancel') }}
- </button>
- <button
- :class="`btn-${footerPrimaryButtonVariant}`"
- type="button"
- class="btn js-modal-primary-action qa-modal-primary-button"
- data-dismiss="modal"
- @click="emitSubmit($event)"
- >
- {{ footerPrimaryButtonText }}
- </button>
- </slot>
- </div>
- </div>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue
index 4b91d4c00e3..438851e5ac7 100644
--- a/app/assets/javascripts/vue_shared/components/gl_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue
@@ -1,6 +1,117 @@
<script>
-// This file was only introduced to not break master and shall be delete soon.
-import DeprecatedModal2 from '~/vue_shared/components/deprecated_modal_2.vue';
+import $ from 'jquery';
-export default DeprecatedModal2;
+const buttonVariants = ['danger', 'primary', 'success', 'warning'];
+const sizeVariants = ['sm', 'md', 'lg', 'xl'];
+
+export default {
+ name: 'GlModal',
+ props: {
+ id: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ modalSize: {
+ type: String,
+ required: false,
+ default: 'md',
+ validator: value => sizeVariants.includes(value),
+ },
+ headerTitleText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ footerPrimaryButtonVariant: {
+ type: String,
+ required: false,
+ default: 'primary',
+ validator: value => buttonVariants.includes(value),
+ },
+ footerPrimaryButtonText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ modalSizeClass() {
+ return this.modalSize === 'md' ? '' : `modal-${this.modalSize}`;
+ },
+ },
+ mounted() {
+ $(this.$el)
+ .on('shown.bs.modal', this.opened)
+ .on('hidden.bs.modal', this.closed);
+ },
+ beforeDestroy() {
+ $(this.$el)
+ .off('shown.bs.modal', this.opened)
+ .off('hidden.bs.modal', this.closed);
+ },
+ methods: {
+ emitCancel(event) {
+ this.$emit('cancel', event);
+ },
+ emitSubmit(event) {
+ this.$emit('submit', event);
+ },
+ opened() {
+ this.$emit('open');
+ },
+ closed() {
+ this.$emit('closed');
+ },
+ },
+};
</script>
+
+<template>
+ <div :id="id" class="modal fade" tabindex="-1" role="dialog">
+ <div :class="modalSizeClass" class="modal-dialog" role="document">
+ <div class="modal-content">
+ <div class="modal-header">
+ <slot name="header">
+ <h4 class="modal-title">
+ <slot name="title"> {{ headerTitleText }} </slot>
+ </h4>
+ <button
+ :aria-label="s__('Modal|Close')"
+ type="button"
+ class="close js-modal-close-action"
+ data-dismiss="modal"
+ @click="emitCancel($event)"
+ >
+ <span aria-hidden="true">&times;</span>
+ </button>
+ </slot>
+ </div>
+
+ <div class="modal-body"><slot></slot></div>
+
+ <div class="modal-footer">
+ <slot name="footer">
+ <button
+ type="button"
+ class="btn js-modal-cancel-action qa-modal-cancel-button"
+ data-dismiss="modal"
+ @click="emitCancel($event)"
+ >
+ {{ s__('Modal|Cancel') }}
+ </button>
+ <button
+ :class="`btn-${footerPrimaryButtonVariant}`"
+ type="button"
+ class="btn js-modal-primary-action qa-modal-primary-button"
+ data-dismiss="modal"
+ @click="emitSubmit($event)"
+ >
+ {{ footerPrimaryButtonText }}
+ </button>
+ </slot>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue b/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
index e89638130f5..1e2d4ffa7e3 100644
--- a/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
+++ b/app/assets/javascripts/vue_shared/components/pagination/table_pagination.vue
@@ -27,7 +27,8 @@ export default {
/**
pageInfo will come from the headers of the API call
- there should be a function that constructs the pageInfo for this component
+ in the `.then` clause of the VueResource API call
+ there should be a function that contructs the pageInfo for this component
This is an example:
diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue b/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue
index 478e44d104c..7f0345c7ec0 100644
--- a/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue
+++ b/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue
@@ -52,7 +52,7 @@ export default {
this.$emit('projectClicked', project);
},
isSelected(project) {
- return Boolean(_.find(this.selectedProjects, { id: project.id }));
+ return Boolean(_.findWhere(this.selectedProjects, { id: project.id }));
},
onInput: _.debounce(function debouncedOnInput() {
this.$emit('searched', this.searchQuery);
diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
new file mode 100644
index 00000000000..754025207c8
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
@@ -0,0 +1,34 @@
+import Vue from 'vue';
+import VueResource from 'vue-resource';
+import csrf from '../lib/utils/csrf';
+
+Vue.use(VueResource);
+
+// Maintain a global counter for active requests
+// see: spec/support/wait_for_requests.rb
+Vue.http.interceptors.push((request, next) => {
+ window.activeVueResources = window.activeVueResources || 0;
+ window.activeVueResources += 1;
+
+ next(() => {
+ window.activeVueResources -= 1;
+ });
+});
+
+// Inject CSRF token and parse headers.
+// New Vue Resource version uses Headers, we are expecting a plain object to render pagination
+// and polling.
+Vue.http.interceptors.push((request, next) => {
+ request.headers.set(csrf.headerKey, csrf.token);
+
+ next(response => {
+ // Headers object has a `forEach` property that iterates through all values.
+ const headers = {};
+
+ response.headers.forEach((value, key) => {
+ headers[key] = value;
+ });
+ // eslint-disable-next-line no-param-reassign
+ response.headers = headers;
+ });
+});
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index c4a0ed63080..82b4ec750ff 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -28,7 +28,6 @@
@import 'framework/issue_box';
@import 'framework/lists';
@import 'framework/logo';
-@import 'framework/job_log';
@import 'framework/markdown_area';
@import 'framework/media_object';
@import 'framework/modal';
diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
index 05ae9b1d1ed..3238b01c6c0 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
@@ -141,7 +141,7 @@
}
.sidebar-top-level-items > li > a {
- min-height: 45px;
+ min-height: 44px;
}
.fly-out-top-item {
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index 13a2a74fdab..7e7b08797b2 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -12,7 +12,7 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
position: sticky;
position: -webkit-sticky;
top: $flash-container-top;
- z-index: 251;
+ z-index: 200;
.flash-content {
box-shadow: 0 2px 4px 0 $notification-box-shadow-color;
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index 2289f0a7011..81ccea1e01f 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -245,7 +245,7 @@
.select2-highlighted {
.group-result {
.group-path {
- color: $gray-800;
+ color: $white-light;
}
}
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index b9cfcf6ce5c..43d0e51e4c9 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -171,7 +171,7 @@
position: absolute;
top: $gl-padding;
bottom: $gl-padding;
- left: map-get($spacers, 2) - px-to-rem(1px);
+ left: map-get($spacers, 2) - 1px;
}
&-row {
@@ -187,7 +187,7 @@
* 2px extra is to give a little more height than needed
* to hide timeline line before/after the element starts/ends
*/
- height: map-get($spacers, 4) + px-to-rem(2px);
+ height: map-get($spacers, 4) + 2px;
z-index: 1;
position: relative;
top: -3px;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 7577112cb0e..801e9e7204c 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -877,16 +877,11 @@ pre.light-well {
flex-direction: column;
// Disable Flexbox for admin page
- &.admin-projects,
- &.group-settings-projects {
+ &.admin-projects {
display: block;
.project-row {
display: block;
-
- .description > p {
- margin-bottom: 0;
- }
}
}
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 80c0a0d88a8..7012bfcefe3 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -78,8 +78,8 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def todos_counts
{
- count: current_user.todos_pending_count,
- done_count: current_user.todos_done_count
+ count: number_with_delimiter(current_user.todos_pending_count),
+ done_count: number_with_delimiter(current_user.todos_done_count)
}
end
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index c418b11ab13..72f830fc9a1 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -2,7 +2,6 @@
class Import::GithubController < Import::BaseController
include ImportHelper
- include ActionView::Helpers::SanitizeHelper
before_action :verify_import_enabled
before_action :provider_auth, only: [:status, :realtime_changes, :create]
@@ -56,7 +55,7 @@ class Import::GithubController < Import::BaseController
def realtime_changes
Gitlab::PollingInterval.set_header(response, interval: 3_000)
- render json: already_added_projects.to_json(only: [:id], methods: [:import_status])
+ render json: find_jobs(provider)
end
private
@@ -83,7 +82,7 @@ class Import::GithubController < Import::BaseController
end
def already_added_projects
- @already_added_projects ||= filtered(find_already_added_projects(provider))
+ @already_added_projects ||= find_already_added_projects(provider)
end
def already_added_project_names
@@ -105,7 +104,7 @@ class Import::GithubController < Import::BaseController
end
def client_repos
- @client_repos ||= filtered(client.repos)
+ @client_repos ||= client.repos
end
def verify_import_enabled
@@ -186,20 +185,6 @@ class Import::GithubController < Import::BaseController
def extra_import_params
{}
end
-
- def sanitized_filter_param
- @filter ||= sanitize(params[:filter])
- end
-
- def filter_attribute
- :name
- end
-
- def filtered(collection)
- return collection unless sanitized_filter_param
-
- collection.select { |item| item[filter_attribute].include?(sanitized_filter_param) }
- end
end
Import::GithubController.prepend_if_ee('EE::Import::GithubController')
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index 50399a8cfbb..da8a371acaa 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -8,37 +8,10 @@ class Projects::ArtifactsController < Projects::ApplicationController
layout 'project'
before_action :authorize_read_build!
before_action :authorize_update_build!, only: [:keep]
- before_action :authorize_destroy_artifacts!, only: [:destroy]
before_action :extract_ref_name_and_path
- before_action :validate_artifacts!, except: [:index, :download, :destroy]
+ before_action :validate_artifacts!, except: [:download]
before_action :entry, only: [:file]
- MAX_PER_PAGE = 20
-
- def index
- # Loading artifacts is very expensive in projects with a lot of artifacts.
- # This feature flag prevents a DOS attack vector.
- # It should be removed only after resolving the underlying performance
- # issues: https://gitlab.com/gitlab-org/gitlab/issues/32281
- return head :no_content unless Feature.enabled?(:artifacts_management_page, @project)
-
- finder = ArtifactsFinder.new(@project, artifacts_params)
- all_artifacts = finder.execute
-
- @artifacts = all_artifacts.page(params[:page]).per(MAX_PER_PAGE)
- @total_size = all_artifacts.total_size
- end
-
- def destroy
- notice = if artifact.destroy
- _('Artifact was successfully deleted.')
- else
- _('Artifact could not be deleted.')
- end
-
- redirect_to project_artifacts_path(@project), status: :see_other, notice: notice
- end
-
def download
return render_404 unless artifacts_file
@@ -101,10 +74,6 @@ class Projects::ArtifactsController < Projects::ApplicationController
@ref_name, @path = extract_ref(params[:ref_name_and_path])
end
- def artifacts_params
- params.permit(:sort)
- end
-
def validate_artifacts!
render_404 unless build&.artifacts?
end
@@ -116,11 +85,6 @@ class Projects::ArtifactsController < Projects::ApplicationController
end
end
- def artifact
- @artifact ||=
- project.job_artifacts.find(params[:id])
- end
-
def build_from_id
project.builds.find_by_id(params[:job_id]) if params[:job_id]
end
diff --git a/app/controllers/projects/registry/tags_controller.rb b/app/controllers/projects/registry/tags_controller.rb
index b5ee0ba9beb..54e2faa2dd7 100644
--- a/app/controllers/projects/registry/tags_controller.rb
+++ b/app/controllers/projects/registry/tags_controller.rb
@@ -19,12 +19,14 @@ module Projects
end
def destroy
- result = Projects::ContainerRepository::DeleteTagsService
- .new(image.project, current_user, tags: [params[:id]])
- .execute(image)
-
- respond_to do |format|
- format.json { head(result[:status] == :success ? :ok : bad_request) }
+ if tag.delete
+ respond_to do |format|
+ format.json { head :no_content }
+ end
+ else
+ respond_to do |format|
+ format.json { head :bad_request }
+ end
end
end
@@ -40,12 +42,21 @@ module Projects
return
end
- result = Projects::ContainerRepository::DeleteTagsService
- .new(image.project, current_user, tags: tag_names)
- .execute(image)
+ @tags = tag_names.map { |tag_name| image.tag(tag_name) }
+ unless @tags.all? { |tag| tag.valid_name? }
+ head :bad_request
+ return
+ end
+
+ success_count = 0
+ @tags.each do |tag|
+ if tag.delete
+ success_count += 1
+ end
+ end
respond_to do |format|
- format.json { head(result[:status] == :success ? :no_content : :bad_request) }
+ format.json { head(success_count == @tags.size ? :no_content : :bad_request) }
end
end
@@ -59,6 +70,10 @@ module Projects
@image ||= project.container_repositories
.find(params[:repository_id])
end
+
+ def tag
+ @tag ||= image.tag(params[:id])
+ end
end
end
end
diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb
index 95739f96d39..f987033a26c 100644
--- a/app/controllers/projects/templates_controller.rb
+++ b/app/controllers/projects/templates_controller.rb
@@ -13,14 +13,6 @@ class Projects::TemplatesController < Projects::ApplicationController
end
end
- def names
- templates = @template_type.dropdown_names(project)
-
- respond_to do |format|
- format.json { render json: templates }
- end
- end
-
private
# User must have:
diff --git a/app/finders/artifacts_finder.rb b/app/finders/artifacts_finder.rb
deleted file mode 100644
index 81c5168d782..00000000000
--- a/app/finders/artifacts_finder.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-# frozen_string_literal: true
-
-class ArtifactsFinder
- def initialize(project, params = {})
- @project = project
- @params = params
- end
-
- def execute
- artifacts = @project.job_artifacts
-
- sort(artifacts)
- end
-
- private
-
- def sort_key
- @params[:sort] || 'created_desc'
- end
-
- def sort(artifacts)
- artifacts.order_by(sort_key)
- end
-end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 24614b5030c..014523b54cb 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -272,7 +272,7 @@ module IssuablesHelper
markdownPreviewPath: preview_markdown_path(parent),
markdownDocsPath: help_page_path('user/markdown'),
lockVersion: issuable.lock_version,
- issuableTemplateNamesPath: template_names_path(parent, issuable),
+ issuableTemplates: issuable_templates(issuable),
initialTitleHtml: markdown_field(issuable, :title),
initialTitleText: issuable.title,
initialDescriptionHtml: markdown_field(issuable, :description),
@@ -429,12 +429,6 @@ module IssuablesHelper
end
end
- def template_names_path(parent, issuable)
- return '' unless parent.is_a?(Project)
-
- project_template_names_path(parent, template_type: issuable.class.name.underscore)
- end
-
def issuable_sidebar_options(issuable)
{
endpoint: "#{issuable[:issuable_json_path]}?serializer=sidebar_extras",
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index c65611b7efc..0f4e5adca6c 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -34,15 +34,15 @@ module SearchHelper
from: from,
to: to,
count: count,
- scope: search_entries_scope_label(scope, count),
+ scope: search_entries_info_label(scope, count),
term: term
}
end
- def search_entries_scope_label(scope, count)
+ def search_entries_info_label(scope, count)
case scope
- when 'blobs'
- ns_('SearchResults|code result', 'SearchResults|code results', count)
+ when 'blobs', 'snippet_blobs', 'wiki_blobs'
+ ns_('SearchResults|result', 'SearchResults|results', count)
when 'commits'
ns_('SearchResults|commit', 'SearchResults|commits', count)
when 'issues'
@@ -55,14 +55,10 @@ module SearchHelper
ns_('SearchResults|comment', 'SearchResults|comments', count)
when 'projects'
ns_('SearchResults|project', 'SearchResults|projects', count)
- when 'snippet_blobs'
- ns_('SearchResults|snippet result', 'SearchResults|snippet results', count)
when 'snippet_titles'
ns_('SearchResults|snippet', 'SearchResults|snippets', count)
when 'users'
ns_('SearchResults|user', 'SearchResults|users', count)
- when 'wiki_blobs'
- ns_('SearchResults|wiki result', 'SearchResults|wiki results', count)
else
raise "Unrecognized search scope '#{scope}'"
end
@@ -76,13 +72,6 @@ module SearchHelper
end
end
- def search_entries_empty_message(scope, term)
- (s_("SearchResults|We couldn't find any %{scope} matching %{term}") % {
- scope: search_entries_scope_label(scope, 0),
- term: "<code>#{term}</code>"
- }).html_safe
- end
-
def find_project_for_result_blob(projects, result)
@project
end
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index 33f3bb0b749..d680e10525d 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -28,9 +28,7 @@ module SortingHelper
sort_value_priority => sort_title_priority,
sort_value_upvotes => sort_title_upvotes,
sort_value_contacted_date => sort_title_contacted_date,
- sort_value_relative_position => sort_title_relative_position,
- sort_value_size => sort_title_size,
- sort_value_expire_date => sort_title_expire_date
+ sort_value_relative_position => sort_title_relative_position
}
end
@@ -408,14 +406,6 @@ module SortingHelper
s_('SortOptions|Manual')
end
- def sort_title_size
- s_('SortOptions|Size')
- end
-
- def sort_title_expire_date
- s_('SortOptions|Expired date')
- end
-
# Values.
def sort_value_access_level_asc
'access_level_asc'
@@ -568,12 +558,4 @@ module SortingHelper
def sort_value_relative_position
'relative_position'
end
-
- def sort_value_size
- 'size_desc'
- end
-
- def sort_value_expire_date
- 'expired_asc'
- end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 3ef0ed0ef49..1f8a0373450 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -118,6 +118,8 @@ module Ci
scope :eager_load_job_artifacts, -> { includes(:job_artifacts) }
+ scope :with_artifacts_stored_locally, -> { with_existing_job_artifacts(Ci::JobArtifact.archive.with_files_stored_locally) }
+ scope :with_archived_trace_stored_locally, -> { with_existing_job_artifacts(Ci::JobArtifact.trace.with_files_stored_locally) }
scope :with_artifacts_not_expired, ->() { with_artifacts_archive.where('artifacts_expire_at IS NULL OR artifacts_expire_at > ?', Time.now) }
scope :with_expired_artifacts, ->() { with_artifacts_archive.where('artifacts_expire_at < ?', Time.now) }
scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index 62bf2c3ac9c..da2758507ce 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -5,7 +5,6 @@ module Ci
include AfterCommitQueue
include ObjectStorage::BackgroundMove
include UpdateProjectStatistics
- include Sortable
extend Gitlab::Ci::Model
NotSupportedAdapterError = Class.new(StandardError)
@@ -65,7 +64,6 @@ module Ci
after_save :update_file_store, if: :saved_change_to_file?
scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) }
- scope :with_files_stored_remotely, -> { where(file_store: ::JobArtifactUploader::Store::REMOTE) }
scope :with_file_types, -> (file_types) do
types = self.file_types.select { |file_type| file_types.include?(file_type) }.values
@@ -145,10 +143,6 @@ module Ci
self.update_column(:file_store, file.object_store)
end
- def self.total_size
- self.sum(:size)
- end
-
def self.artifacts_size_for(project)
self.where(project: project).sum(:size)
end
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 231cadfae05..6a5b98a4676 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -39,7 +39,7 @@ module Clusters
def self.has_one_cluster_application(name) # rubocop:disable Naming/PredicateName
application = APPLICATIONS[name.to_s]
- has_one application.association_name, class_name: application.to_s, inverse_of: :cluster # rubocop:disable Rails/ReflectionClassName
+ has_one application.association_name, class_name: application.to_s # rubocop:disable Rails/ReflectionClassName
end
has_one_cluster_application :helm
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
index f73aa2705b7..60b11ad9356 100644
--- a/app/models/lfs_object.rb
+++ b/app/models/lfs_object.rb
@@ -9,7 +9,6 @@ class LfsObject < ApplicationRecord
has_many :projects, -> { distinct }, through: :lfs_objects_projects
scope :with_files_stored_locally, -> { where(file_store: LfsObjectUploader::Store::LOCAL) }
- scope :with_files_stored_remotely, -> { where(file_store: LfsObjectUploader::Store::REMOTE) }
validates :oid, presence: true, uniqueness: true
diff --git a/app/models/project.rb b/app/models/project.rb
index 18afccf7ddc..7c065db9829 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -273,7 +273,6 @@ class Project < ApplicationRecord
has_many :builds, class_name: 'Ci::Build', inverse_of: :project, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :build_trace_section_names, class_name: 'Ci::BuildTraceSectionName'
has_many :build_trace_chunks, class_name: 'Ci::BuildTraceChunk', through: :builds, source: :trace_chunks
- has_many :job_artifacts, class_name: 'Ci::JobArtifact'
has_many :runner_projects, class_name: 'Ci::RunnerProject', inverse_of: :project
has_many :runners, through: :runner_projects, source: :runner, class_name: 'Ci::Runner'
has_many :variables, class_name: 'Ci::Variable'
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 86139c62f68..a76970bfa2a 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -298,7 +298,7 @@ class JiraService < IssueTrackerService
title: title,
status: status,
icon: {
- title: 'GitLab', url16x16: asset_url(Gitlab::Favicon.main, host: gitlab_config.base_url)
+ title: 'GitLab', url16x16: asset_url(Gitlab::Favicon.main, host: gitlab_config.url)
}
}
}
diff --git a/app/models/release.rb b/app/models/release.rb
index 9117a475ee9..cd63b4d5fef 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -22,6 +22,7 @@ class Release < ApplicationRecord
accepts_nested_attributes_for :links, allow_destroy: true
validates :description, :project, :tag, presence: true
+ validates :name, presence: true, on: :create
validates_associated :milestone_releases, message: -> (_, obj) { obj[:value].map(&:errors).map(&:full_messages).join(",") }
scope :sorted, -> { order(released_at: :desc) }
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 96b1b55e2b1..f084a314392 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -133,28 +133,18 @@ class Repository
end
end
- # the opts are:
- # - :path
- # - :limit
- # - :offset
- # - :skip_merges
- # - :after
- # - :before
- # - :all
- # - :first_parent
- def commits(ref = nil, opts = {})
+ def commits(ref = nil, path: nil, limit: nil, offset: nil, skip_merges: false, after: nil, before: nil, all: nil)
options = {
repo: raw_repository,
ref: ref,
- path: opts[:path],
- follow: Array(opts[:path]).length == 1,
- limit: opts[:limit],
- offset: opts[:offset],
- skip_merges: !!opts[:skip_merges],
- after: opts[:after],
- before: opts[:before],
- all: !!opts[:all],
- first_parent: !!opts[:first_parent]
+ path: path,
+ limit: limit,
+ offset: offset,
+ after: after,
+ before: before,
+ follow: Array(path).length == 1,
+ skip_merges: skip_merges,
+ all: all
}
commits = Gitlab::Git::Commit.where(options)
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index 6d370f6241c..6c300cd8be1 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -51,8 +51,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
new_file_anchor_data,
readme_anchor_data,
changelog_anchor_data,
- contribution_guide_anchor_data,
- gitlab_ci_anchor_data
+ contribution_guide_anchor_data
].compact.reject { |item| item.is_link }
end
diff --git a/app/services/import_export_clean_up_service.rb b/app/services/import_export_clean_up_service.rb
index 66ac7dac4ca..3ecb51b60d0 100644
--- a/app/services/import_export_clean_up_service.rb
+++ b/app/services/import_export_clean_up_service.rb
@@ -12,20 +12,16 @@ class ImportExportCleanUpService
def execute
Gitlab::Metrics.measure(:import_export_clean_up) do
- execute_cleanup
+ clean_up_export_object_files
+
+ break unless File.directory?(path)
+
+ clean_up_export_files
end
end
private
- def execute_cleanup
- clean_up_export_object_files
- ensure
- # We don't want a failure in cleaning up object storage from
- # blocking us from cleaning up temporary storage.
- clean_up_export_files if File.directory?(path)
- end
-
def clean_up_export_files
Gitlab::Popen.popen(%W(find #{path} -not -path #{path} -mmin +#{mmin} -delete))
end
diff --git a/app/services/projects/container_repository/cleanup_tags_service.rb b/app/services/projects/container_repository/cleanup_tags_service.rb
index 1b880a7aab1..d1d9b9f22e8 100644
--- a/app/services/projects/container_repository/cleanup_tags_service.rb
+++ b/app/services/projects/container_repository/cleanup_tags_service.rb
@@ -40,7 +40,7 @@ module Projects
return unless tags.count == other_tags.count
# delete all tags
- tags.map(&:unsafe_delete)
+ tags.map(&:delete)
end
def group_by_digest(tags)
diff --git a/app/services/projects/container_repository/delete_tags_service.rb b/app/services/projects/container_repository/delete_tags_service.rb
deleted file mode 100644
index 21dc1621e5c..00000000000
--- a/app/services/projects/container_repository/delete_tags_service.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: true
-
-module Projects
- module ContainerRepository
- class DeleteTagsService < BaseService
- def execute(container_repository)
- return error('access denied') unless can?(current_user, :destroy_container_image, project)
-
- tag_names = params[:tags]
- return error('not tags specified') if tag_names.blank?
-
- if can_use?
- smart_delete(container_repository, tag_names)
- else
- unsafe_delete(container_repository, tag_names)
- end
- end
-
- private
-
- def unsafe_delete(container_repository, tag_names)
- deleted_tags = tag_names.select do |tag_name|
- container_repository.tag(tag_name).unsafe_delete
- end
-
- return error('could not delete tags') if deleted_tags.empty?
-
- success(deleted: deleted_tags)
- end
-
- # Replace a tag on the registry with a dummy tag.
- # This is a hack as the registry doesn't support deleting individual
- # tags. This code effectively pushes a dummy image and assigns the tag to it.
- # This way when the tag is deleted only the dummy image is affected.
- # See https://gitlab.com/gitlab-org/gitlab-ce/issues/21405 for a discussion
- def smart_delete(container_repository, tag_names)
- # generates the blobs for the dummy image
- dummy_manifest = container_repository.client.generate_empty_manifest(container_repository.path)
-
- # update the manifests of the tags with the new dummy image
- tag_digests = tag_names.map do |name|
- container_repository.client.put_tag(container_repository.path, name, dummy_manifest)
- end
-
- # make sure the digests are the same (it should always be)
- tag_digests.uniq!
-
- # rubocop: disable CodeReuse/ActiveRecord
- Gitlab::Sentry.track_exception(ArgumentError.new('multiple tag digests')) if tag_digests.many?
-
- # deletes the dummy image
- # all created tag digests are the same since they all have the same dummy image.
- # a single delete is sufficient to remove all tags with it
- if container_repository.client.delete_repository_tag(container_repository.path, tag_digests.first)
- success(deleted: tag_names)
- else
- error('could not delete tags')
- end
- end
-
- def can_use?
- Feature.enabled?(:container_registry_smart_delete, project, default_enabled: true)
- end
- end
- end
-end
diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index 08e668e8623..bb0d62a70c0 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -1,20 +1,8 @@
-- page_title _('Projects')
+- page_title "Projects"
- params[:visibility_level] ||= []
.top-area.scrolling-tabs-container.inner-page-scroll-tabs
- %ul.nav-links.nav.nav-tabs
- - opts = params[:visibility_level].present? ? {} : { page: admin_projects_path }
- = nav_link(opts) do
- = link_to _('All'), admin_projects_path
-
- = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s) }) do
- = link_to _('Private'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
- = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s) }) do
- = link_to _('Internal'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL)
- = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s) }) do
- = link_to _('Public'), admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
-
- .nav-controls
+ .prepend-top-default
.search-holder
= render 'shared/projects/search_form', autofocus: true, admin_view: true
.dropdown
@@ -34,4 +22,20 @@
New Project
= button_tag "Search", class: "btn btn-primary btn-search hide"
+ %ul.nav-links.nav.nav-tabs
+ - opts = params[:visibility_level].present? ? {} : { page: admin_projects_path }
+ = nav_link(opts) do
+ = link_to admin_projects_path do
+ All
+
+ = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PRIVATE.to_s) }) do
+ = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PRIVATE) do
+ Private
+ = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::INTERNAL.to_s) }) do
+ = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::INTERNAL) do
+ Internal
+ = nav_link(html_options: { class: active_when(params[:visibility_level] == Gitlab::VisibilityLevel::PUBLIC.to_s) }) do
+ = link_to admin_projects_path(visibility_level: Gitlab::VisibilityLevel::PUBLIC) do
+ Public
+
= render 'projects'
diff --git a/app/views/ci/runner/_how_to_setup_runner_automatically.html.haml b/app/views/ci/runner/_how_to_setup_runner_automatically.html.haml
deleted file mode 100644
index 58d2ef5d5e6..00000000000
--- a/app/views/ci/runner/_how_to_setup_runner_automatically.html.haml
+++ /dev/null
@@ -1,22 +0,0 @@
-.append-bottom-10
- %h4= _('Set up a %{type} Runner automatically') % { type: type }
-
-%p
- - link_to_help_page = link_to(_('Learn more about Kubernetes'),
- help_page_path('user/project/clusters/index'),
- target: '_blank',
- rel: 'noopener noreferrer')
-
- = _('You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page }
-
-%ol
- %li
- = _('Click the button below to begin the install process by navigating to the Kubernetes page')
- %li
- = _('Select an existing Kubernetes cluster or create a new one')
- %li
- = _('From the Kubernetes cluster details view, install Runner from the applications list')
-
-= link_to _('Install Runner on Kubernetes'),
- clusters_path,
- class: 'btn btn-info'
diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml
index cccba48624b..00fdd5e9562 100644
--- a/app/views/clusters/clusters/show.html.haml
+++ b/app/views/clusters/clusters/show.html.haml
@@ -3,7 +3,6 @@
- breadcrumb_title @cluster.name
- page_title _('Kubernetes Cluster')
- manage_prometheus_path = edit_project_service_path(@cluster.project, 'prometheus') if @project
-- cluster_environments_path = clusterable.environments_cluster_path(@cluster)
- expanded = expanded_by_default?
@@ -17,7 +16,7 @@
install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter),
install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative),
update_knative_path: clusterable.update_applications_cluster_path(@cluster, :knative),
- cluster_environments_path: cluster_environments_path,
+ cluster_environments_path: clusterable.environments_cluster_path(@cluster),
toggle_status: @cluster.enabled? ? 'true': 'false',
has_rbac: has_rbac_enabled?(@cluster) ? 'true': 'false',
cluster_type: @cluster.cluster_type,
@@ -38,7 +37,7 @@
%h4= @cluster.name
= render 'banner'
- - if cluster_environments_path.present?
- = render_if_exists 'clusters/clusters/group_cluster_environments', expanded: expanded
- - else
+ = render_if_exists 'clusters/clusters/group_cluster_environments', expanded: expanded
+
+ - unless Gitlab.ee?
= render 'configure', expanded: expanded
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index 8b01e54474a..ba186875a86 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -8,38 +8,21 @@
.controls
= link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
New project
- %ul.projects-list.content-list.group-settings-projects
+ %ul.content-list
- @projects.each do |project|
- %li.project-row{ class: ('no-description' if project.description.blank?) }
- .controls
- = link_to _('Members'), project_project_members_path(project), id: "edit_#{dom_id(project)}", class: "btn"
- = link_to _('Edit'), edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn"
- = link_to _('Remove'), project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-remove"
-
- .stats
- %span.badge.badge-pill
- = storage_counter(project.statistics&.storage_size)
+ %li
+ .list-item-name
+ %span{ class: visibility_level_color(project.visibility_level) }
+ = visibility_level_icon(project.visibility_level)
+ %strong= link_to project.full_name, project
+ .float-right
- if project.archived
%span.badge.badge-warning archived
-
- .title
- = link_to(project_path(project)) do
- .dash-project-avatar
- .avatar-container.rect-avatar.s40
- = project_icon(project, alt: '', class: 'avatar project-avatar s40', width: 40, height: 40)
- %span.project-full-name
- %span.namespace-name
- - if project.namespace
- = project.namespace.human_name
- \/
- %span.project-name
- = project.name
- %span{ class: visibility_level_color(project.visibility_level) }
- = visibility_level_icon(project.visibility_level)
-
- - if project.description.present?
- .description
- = markdown_field(project, :description)
+ %span.badge.badge-pill
+ = storage_counter(project.statistics.storage_size)
+ = link_to 'Members', project_project_members_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
+ = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-sm"
+ = link_to 'Remove', project, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-sm btn-remove"
- if @projects.blank?
.nothing-here-block This group has no projects yet
diff --git a/app/views/groups/runners/_group_runners.html.haml b/app/views/groups/runners/_group_runners.html.haml
index f752bc0a702..fd40ec5a984 100644
--- a/app/views/groups/runners/_group_runners.html.haml
+++ b/app/views/groups/runners/_group_runners.html.haml
@@ -10,10 +10,6 @@
-# Proper policies should be implemented per
-# https://gitlab.com/gitlab-org/gitlab-foss/issues/45894
- if can?(current_user, :admin_pipeline, @group)
- = render partial: 'ci/runner/how_to_setup_runner_automatically',
- locals: { type: 'group',
- clusters_path: group_clusters_path(@group) }
- %hr
= render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: @group.runners_token,
type: 'group',
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 6cdb85456c3..443a73f5cce 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -17,6 +17,4 @@
%div{ class: "#{(container_class unless @no_container)} #{@content_class}" }
.content{ id: "content-body" }
= render "layouts/flash", extra_flash_class: 'limit-container-width'
- - if Gitlab.com?
- = render_if_exists "layouts/privacy_policy_update_callout"
= yield
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 367b8c1138e..de0c21e7cf6 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -43,10 +43,7 @@
.issuable-meta
%ul.controls
- - if issue.moved?
- %li.issuable-status
- = _('CLOSED (MOVED)')
- - elsif issue.closed?
+ - if issue.closed?
%li.issuable-status
= _('CLOSED')
- if issue.assignees.any?
diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
index 80d2d2afada..104c68919f0 100644
--- a/app/views/projects/mirrors/_mirror_repos.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -1,7 +1,7 @@
- expanded = expanded_by_default?
- protocols = Gitlab::UrlSanitizer::ALLOWED_SCHEMES.join('|')
-%section.settings.project-mirror-settings.js-mirror-settings.no-animate#js-push-remote-settings{ class: ('expanded' if expanded), data: { qa_selector: 'mirroring_repositories_settings_section' } }
+%section.settings.project-mirror-settings.js-mirror-settings.no-animate.qa-mirroring-repositories-settings#js-push-remote-settings{ class: ('expanded' if expanded) }
.settings-header
%h4= _('Mirroring repositories')
%button.btn.js-settings-toggle
@@ -59,10 +59,10 @@
- if mirror.disabled?
= render 'projects/mirrors/disabled_mirror_badge'
- if mirror.last_error.present?
- .badge.mirror-error-badge{ data: { toggle: 'tooltip', html: 'true', qa_selector: 'mirror_error_badge' }, title: html_escape(mirror.last_error.try(:strip)) }= _('Error')
+ .badge.mirror-error-badge{ data: { toggle: 'tooltip', html: 'true' }, title: html_escape(mirror.last_error.try(:strip)) }= _('Error')
%td
.btn-group.mirror-actions-group.pull-right{ role: 'group' }
- if mirror.ssh_key_auth?
- = clipboard_button(text: mirror.ssh_public_key, class: 'btn btn-default', title: _('Copy SSH public key'), qa_selector: 'copy_public_key_button')
+ = clipboard_button(text: mirror.ssh_public_key, class: 'btn btn-default', title: _('Copy SSH public key'))
= render 'shared/remote_mirror_update_button', remote_mirror: mirror
%button.js-delete-mirror.qa-delete-mirror.rspec-delete-mirror.btn.btn-danger{ type: 'button', data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' }, title: _('Remove') }= icon('trash-o')
diff --git a/app/views/projects/mirrors/_ssh_host_keys.html.haml b/app/views/projects/mirrors/_ssh_host_keys.html.haml
index 3279d3eb251..7762fb4b844 100644
--- a/app/views/projects/mirrors/_ssh_host_keys.html.haml
+++ b/app/views/projects/mirrors/_ssh_host_keys.html.haml
@@ -3,13 +3,13 @@
- verified_at = mirror.ssh_known_hosts_verified_at
.form-group.js-ssh-host-keys-section{ class: ('collapse' unless mirror.ssh_mirror_url?) }
- %button.btn.btn-inverted.btn-secondary.inline.js-detect-host-keys.append-right-10{ type: 'button', data: { qa_selector: 'detect_host_keys' } }
+ %button.btn.btn-inverted.btn-secondary.inline.js-detect-host-keys.append-right-10{ type: 'button' }
= icon('spinner spin', class: 'js-spinner d-none')
= _('Detect host keys')
.fingerprint-ssh-info.js-fingerprint-ssh-info.prepend-top-10.append-bottom-10{ class: ('collapse' unless mirror.ssh_mirror_url?) }
%label.label-bold
= _('Fingerprints')
- .fingerprints-list.js-fingerprints-list{ data: { qa_selector: 'fingerprints_list' } }
+ .fingerprints-list.js-fingerprints-list
- mirror.ssh_known_hosts_fingerprints.each do |fp|
%code= fp.fingerprint
- if verified_at
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 4cc67a8f5d8..dc56a515d4c 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -2,9 +2,28 @@
= _('Specific Runners')
.bs-callout.help-callout
- = render partial: 'ci/runner/how_to_setup_runner_automatically',
- locals: { type: 'specific',
- clusters_path: project_clusters_path(@project) }
+ .append-bottom-10
+ %h4= _('Set up a specific Runner automatically')
+
+ %p
+ - link_to_help_page = link_to(_('Learn more about Kubernetes'),
+ help_page_path('user/project/clusters/index'),
+ target: '_blank',
+ rel: 'noopener noreferrer')
+
+ = _('You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page }
+
+ %ol
+ %li
+ = _('Click the button below to begin the install process by navigating to the Kubernetes page')
+ %li
+ = _('Select an existing Kubernetes cluster or create a new one')
+ %li
+ = _('From the Kubernetes cluster details view, install Runner from the applications list')
+
+ = link_to _('Install Runner on Kubernetes'),
+ project_clusters_path(@project),
+ class: 'btn btn-info'
%hr
= render partial: 'ci/runner/how_to_setup_runner',
locals: { registration_token: @project.runners_token,
diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
index 6702786fdb3..1d5d90593ae 100644
--- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
@@ -46,12 +46,12 @@
= form.radio_button :deploy_strategy, 'timed_incremental', class: 'form-check-input'
= form.label :deploy_strategy_timed_incremental, class: 'form-check-label' do
= s_('CICD|Continuous deployment to production using timed incremental rollout')
- = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'timed-incremental-rollout-to-production-premium'), target: '_blank'
+ = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'timed-incremental-rollout-to-production'), target: '_blank'
.form-check
= form.radio_button :deploy_strategy, 'manual', class: 'form-check-input'
= form.label :deploy_strategy_manual, class: 'form-check-label' do
= s_('CICD|Automatic deployment to staging, manual deployment to production')
- = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'incremental-rollout-to-production-premium'), target: '_blank'
+ = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'incremental-rollout-to-production'), target: '_blank'
= f.submit _('Save changes'), class: "btn btn-success prepend-top-15", data: { qa_selector: 'save_changes_button' }
diff --git a/app/views/search/results/_empty.html.haml b/app/views/search/results/_empty.html.haml
index 6c7c6de1178..9d15995bb51 100644
--- a/app/views/search/results/_empty.html.haml
+++ b/app/views/search/results/_empty.html.haml
@@ -2,4 +2,5 @@
.search_glyph
%h4
= icon('search')
- = search_entries_empty_message(@scope, @search_term)
+ = _("We couldn't find any results matching")
+ %code= @search_term
diff --git a/app/views/shared/_remote_mirror_update_button.html.haml b/app/views/shared/_remote_mirror_update_button.html.haml
index 54bd4ba04a0..4b39c8b06e9 100644
--- a/app/views/shared/_remote_mirror_update_button.html.haml
+++ b/app/views/shared/_remote_mirror_update_button.html.haml
@@ -1,5 +1,5 @@
- if remote_mirror.update_in_progress?
- %button.btn.disabled{ type: 'button', data: { toggle: 'tooltip', container: 'body', qa_selector: 'updating_button' }, title: _('Updating') }
+ %button.btn.disabled{ type: 'button', data: { toggle: 'tooltip', container: 'body' }, title: _('Updating') }
= icon("refresh spin")
- elsif remote_mirror.enabled?
= link_to update_now_project_mirror_path(@project, sync_remote: true), method: :post, class: "btn qa-update-now-button rspec-update-now-button", data: { toggle: 'tooltip', container: 'body' }, title: _('Update now') do