summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/blob/template_selector.js4
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js54
-rw-r--r--app/assets/javascripts/clusters/services/clusters_service.js2
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js1
-rw-r--r--app/assets/javascripts/commit/image_file.js19
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/cluster_form_dropdown.vue182
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue9
-rw-r--r--app/assets/javascripts/create_cluster/eks_cluster/components/role_name_dropdown.vue53
-rw-r--r--app/assets/javascripts/gl_dropdown.js10
-rw-r--r--app/assets/javascripts/issue.js3
-rw-r--r--app/assets/javascripts/jobs/components/log/duration_badge.vue13
-rw-r--r--app/assets/javascripts/jobs/components/log/line.vue2
-rw-r--r--app/assets/javascripts/jobs/components/log/line_header.vue16
-rw-r--r--app/assets/javascripts/jobs/components/log/line_number.vue9
-rw-r--r--app/assets/javascripts/jobs/components/log/log.vue11
-rw-r--r--app/assets/javascripts/jobs/store/utils.js35
-rw-r--r--app/assets/javascripts/label_manager.js5
-rw-r--r--app/assets/javascripts/labels_select.js25
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js4
-rw-r--r--app/assets/javascripts/milestone_select.js8
-rw-r--r--app/assets/javascripts/monitoring/components/charts/column.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/charts/empty_chart.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/charts/single_stat.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/charts/time_series.vue120
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/embed.vue1
-rw-r--r--app/assets/javascripts/network/branch_graph.js12
-rw-r--r--app/assets/javascripts/notes.js44
-rw-r--r--app/assets/javascripts/notes/components/discussion_actions.vue7
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/show/index.js (renamed from app/assets/javascripts/pages/admin/application_settings/general/index.js)0
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js4
-rw-r--r--app/assets/javascripts/performance_bar/services/performance_bar_service.js9
-rw-r--r--app/assets/javascripts/profile/gl_crop.js13
-rw-r--r--app/assets/javascripts/project_find_file.js5
-rw-r--r--app/assets/javascripts/right_sidebar.js14
-rw-r--r--app/assets/javascripts/search_autocomplete.js9
-rw-r--r--app/assets/javascripts/test_utils/simulate_drag.js8
-rw-r--r--app/assets/javascripts/users_select.js7
-rw-r--r--app/assets/javascripts/zen_mode.js4
-rw-r--r--app/assets/stylesheets/framework/flash.scss6
-rw-r--r--app/assets/stylesheets/framework/job_log.scss49
-rw-r--r--app/assets/stylesheets/framework/snippets.scss4
-rw-r--r--app/assets/stylesheets/framework/sortable.scss18
-rw-r--r--app/assets/stylesheets/framework/variables.scss3
-rw-r--r--app/assets/stylesheets/pages/boards.scss14
-rw-r--r--app/assets/stylesheets/pages/issues.scss4
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/pages/projects.scss10
-rw-r--r--app/controllers/admin/application_settings_controller.rb11
-rw-r--r--app/controllers/projects/merge_requests_controller.rb2
-rw-r--r--app/controllers/projects/pipelines_controller.rb10
-rw-r--r--app/finders/issuable_finder.rb65
-rw-r--r--app/graphql/resolvers/issues_resolver.rb19
-rw-r--r--app/graphql/types/order.rb8
-rw-r--r--app/graphql/types/sort.rb10
-rw-r--r--app/graphql/types/sort_enum.rb13
-rw-r--r--app/helpers/page_layout_helper.rb1
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/models/ci/artifact_blob.rb2
-rw-r--r--app/models/ci/build_metadata.rb46
-rw-r--r--app/models/clusters/applications/ingress.rb21
-rw-r--r--app/models/clusters/cluster.rb31
-rw-r--r--app/models/clusters/concerns/application_core.rb4
-rw-r--r--app/models/commit.rb1
-rw-r--r--app/models/concerns/routable.rb20
-rw-r--r--app/models/diff_note.rb13
-rw-r--r--app/models/merge_request.rb17
-rw-r--r--app/models/milestone.rb9
-rw-r--r--app/models/milestone_release.rb4
-rw-r--r--app/models/note_diff_file.rb6
-rw-r--r--app/models/project.rb12
-rw-r--r--app/models/project_services/bugzilla_service.rb2
-rw-r--r--app/models/project_services/custom_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/data_fields.rb52
-rw-r--r--app/models/project_services/gitlab_issue_tracker_service.rb2
-rw-r--r--app/models/project_services/issue_tracker_data.rb3
-rw-r--r--app/models/project_services/issue_tracker_service.rb63
-rw-r--r--app/models/project_services/jira_service.rb33
-rw-r--r--app/models/project_services/jira_tracker_data.rb7
-rw-r--r--app/models/project_services/redmine_service.rb2
-rw-r--r--app/models/project_services/youtrack_service.rb2
-rw-r--r--app/models/protected_branch.rb5
-rw-r--r--app/models/release.rb9
-rw-r--r--app/presenters/ci/build_metadata_presenter.rb3
-rw-r--r--app/services/clusters/applications/base_service.rb4
-rw-r--r--app/services/clusters/applications/create_service.rb2
-rw-r--r--app/services/clusters/applications/destroy_service.rb2
-rw-r--r--app/services/clusters/applications/update_service.rb2
-rw-r--r--app/services/projects/create_from_template_service.rb23
-rw-r--r--app/services/releases/concerns.rb22
-rw-r--r--app/services/releases/create_service.rb4
-rw-r--r--app/services/releases/update_service.rb4
-rw-r--r--app/services/service_response.rb4
-rw-r--r--app/views/admin/application_settings/_account_and_limit.html.haml2
-rw-r--r--app/views/admin/application_settings/_diff_limits.html.haml2
-rw-r--r--app/views/admin/application_settings/_external_authorization_service_form.html.haml2
-rw-r--r--app/views/admin/application_settings/_signin.html.haml2
-rw-r--r--app/views/admin/application_settings/_signup.html.haml2
-rw-r--r--app/views/admin/application_settings/_terminal.html.haml2
-rw-r--r--app/views/admin/application_settings/_terms.html.haml2
-rw-r--r--app/views/admin/application_settings/_visibility_and_access.html.haml2
-rw-r--r--app/views/admin/application_settings/show.html.haml (renamed from app/views/admin/application_settings/general.html.haml)6
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml2
-rw-r--r--app/views/projects/mirrors/_mirror_repos.html.haml6
-rw-r--r--app/views/projects/protected_branches/shared/_create_protected_branch.html.haml2
-rw-r--r--app/views/projects/registry/repositories/index.html.haml2
-rw-r--r--app/views/projects/services/_form.html.haml6
-rw-r--r--app/views/shared/projects/_project.html.haml9
-rw-r--r--app/views/shared/snippets/_form.html.haml2
-rw-r--r--app/views/shared/snippets/_header.html.haml2
110 files changed, 555 insertions, 919 deletions
diff --git a/app/assets/javascripts/blob/template_selector.js b/app/assets/javascripts/blob/template_selector.js
index 9e69c7d7164..37e348d93d3 100644
--- a/app/assets/javascripts/blob/template_selector.js
+++ b/app/assets/javascripts/blob/template_selector.js
@@ -1,4 +1,4 @@
-/* eslint-disable class-methods-use-this */
+/* eslint-disable class-methods-use-this, no-unused-vars */
import $ from 'jquery';
@@ -61,7 +61,7 @@ export default class TemplateSelector {
return this.requestFile(item);
}
- requestFile() {
+ requestFile(item) {
// This `requestFile` method is an abstract method that should
// be added by all subclasses.
}
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index 28850710f80..d386960f3b6 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -111,15 +111,25 @@ export default class Clusters {
this.initApplications(clusterType);
this.initEnvironments();
- if (clusterEnvironmentsPath) {
- this.fetchEnvironments();
+ if (clusterEnvironmentsPath && this.environments) {
+ this.store.toggleFetchEnvironments(true);
+
+ this.initPolling(
+ 'fetchClusterEnvironments',
+ data => this.handleClusterEnvironmentsSuccess(data),
+ () => this.handleEnvironmentsPollError(),
+ );
}
this.updateContainer(null, this.store.state.status, this.store.state.statusReason);
this.addListeners();
if (statusPath && !this.environments) {
- this.initPolling();
+ this.initPolling(
+ 'fetchClusterStatus',
+ data => this.handleClusterStatusSuccess(data),
+ () => this.handlePollError(),
+ );
}
}
@@ -179,16 +189,9 @@ export default class Clusters {
});
}
- fetchEnvironments() {
- this.store.toggleFetchEnvironments(true);
-
- this.service
- .fetchClusterEnvironments()
- .then(data => {
- this.store.toggleFetchEnvironments(false);
- this.store.updateEnvironments(data.data);
- })
- .catch(() => Clusters.handleError());
+ handleClusterEnvironmentsSuccess(data) {
+ this.store.toggleFetchEnvironments(false);
+ this.store.updateEnvironments(data.data);
}
static initDismissableCallout() {
@@ -224,21 +227,16 @@ export default class Clusters {
eventHub.$off('uninstallApplication');
}
- initPolling() {
+ initPolling(method, successCallback, errorCallback) {
this.poll = new Poll({
resource: this.service,
- method: 'fetchData',
- successCallback: data => this.handleSuccess(data),
- errorCallback: () => Clusters.handleError(),
+ method,
+ successCallback,
+ errorCallback,
});
if (!Visibility.hidden()) {
this.poll.makeRequest();
- } else {
- this.service
- .fetchData()
- .then(data => this.handleSuccess(data))
- .catch(() => Clusters.handleError());
}
Visibility.change(() => {
@@ -250,11 +248,21 @@ export default class Clusters {
});
}
+ handlePollError() {
+ this.constructor.handleError();
+ }
+
+ handleEnvironmentsPollError() {
+ this.store.toggleFetchEnvironments(false);
+
+ this.handlePollError();
+ }
+
static handleError() {
Flash(s__('ClusterIntegration|Something went wrong on our end.'));
}
- handleSuccess(data) {
+ handleClusterStatusSuccess(data) {
const prevStatus = this.store.state.status;
const prevApplicationMap = Object.assign({}, this.store.state.applications);
diff --git a/app/assets/javascripts/clusters/services/clusters_service.js b/app/assets/javascripts/clusters/services/clusters_service.js
index 9139e0beafb..fa12802b3de 100644
--- a/app/assets/javascripts/clusters/services/clusters_service.js
+++ b/app/assets/javascripts/clusters/services/clusters_service.js
@@ -17,7 +17,7 @@ export default class ClusterService {
};
}
- fetchData() {
+ fetchClusterStatus() {
return axios.get(this.options.endpoint);
}
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index a032f589ee4..5cddb4cc098 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -218,6 +218,7 @@ export default class ClusterStore {
environmentPath: environment.environment_path,
lastDeployment: environment.last_deployment,
rolloutStatus: {
+ status: environment.rollout_status ? environment.rollout_status.status : null,
instances: environment.rollout_status ? environment.rollout_status.instances : [],
},
updatedAt: environment.updated_at,
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index 9454f760df8..bc666aef54b 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, one-var, no-return-assign, no-unused-expressions, no-sequences */
+/* eslint-disable func-names, no-var, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, one-var, no-unused-vars, no-return-assign, no-unused-expressions, no-sequences */
import $ from 'jquery';
@@ -12,8 +12,11 @@ export default class ImageFile {
this.requestImageInfo(
$('.two-up.view .frame.deleted img', this.file),
(function(_this) {
- return function() {
- return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function() {
+ return function(deletedWidth, deletedHeight) {
+ return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), function(
+ width,
+ height,
+ ) {
_this.initViewModes();
// Load two-up view after images are loaded
@@ -109,7 +112,7 @@ export default class ImageFile {
maxHeight = 0;
$('.frame', view)
.each(
- (function() {
+ (function(_this) {
return function(index, frame) {
var height, width;
width = $(frame).width();
@@ -193,7 +196,13 @@ export default class ImageFile {
return $('.onion-skin.view', this.file).each(
(function(_this) {
return function(index, view) {
- var $frame, $track, $dragger, $frameAdded, framePadding, ref;
+ var $frame,
+ $track,
+ $dragger,
+ $frameAdded,
+ framePadding,
+ ref,
+ dragging = false;
(ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref);
$frame = $('.onion-skin-frame', view);
$frameAdded = $('.frame.added', view);
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/cluster_form_dropdown.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/cluster_form_dropdown.vue
deleted file mode 100644
index f9465da6fda..00000000000
--- a/app/assets/javascripts/create_cluster/eks_cluster/components/cluster_form_dropdown.vue
+++ /dev/null
@@ -1,182 +0,0 @@
-<script>
-import DropdownSearchInput from '~/vue_shared/components/dropdown/dropdown_search_input.vue';
-import DropdownHiddenInput from '~/vue_shared/components/dropdown/dropdown_hidden_input.vue';
-import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue';
-
-export default {
- components: {
- DropdownButton,
- DropdownSearchInput,
- DropdownHiddenInput,
- },
- props: {
- fieldName: {
- type: String,
- required: false,
- default: '',
- },
- placeholder: {
- type: String,
- required: false,
- default: '',
- },
- defaultValue: {
- type: String,
- required: false,
- default: '',
- },
- value: {
- type: Object,
- required: false,
- default: () => null,
- },
- labelProperty: {
- type: String,
- required: false,
- default: 'name',
- },
- valueProperty: {
- type: String,
- required: false,
- default: 'value',
- },
- items: {
- type: Array,
- required: false,
- default: () => [],
- },
- loading: {
- type: Boolean,
- required: false,
- default: false,
- },
- disabled: {
- type: Boolean,
- required: false,
- default: false,
- },
- loadingText: {
- type: String,
- required: false,
- default: '',
- },
- disabledText: {
- type: String,
- required: false,
- default: '',
- },
- hasErrors: {
- type: Boolean,
- required: false,
- default: false,
- },
- errorMessage: {
- type: String,
- required: false,
- default: '',
- },
- searchFieldPlaceholder: {
- type: String,
- required: false,
- default: '',
- },
- emptyText: {
- type: String,
- required: false,
- default: '',
- },
- searchFn: {
- type: Function,
- required: false,
- default: searchQuery => item => item.name.toLowerCase().indexOf(searchQuery) > -1,
- },
- },
- data() {
- return {
- searchQuery: '',
- selectedItem: null,
- };
- },
- computed: {
- toggleText() {
- if (this.loading && this.loadingText) {
- return this.loadingText;
- }
-
- if (this.disabled && this.disabledText) {
- return this.disabledText;
- }
-
- if (!this.selectedItem) {
- return this.placeholder;
- }
-
- return this.selectedItemLabel;
- },
- results() {
- if (!this.items) {
- return [];
- }
-
- return this.items.filter(this.searchFn(this.searchQuery));
- },
- selectedItemLabel() {
- return this.selectedItem && this.selectedItem[this.labelProperty];
- },
- selectedItemValue() {
- return (this.selectedItem && this.selectedItem[this.valueProperty]) || '';
- },
- },
- methods: {
- select(item) {
- this.selectedItem = item;
- this.$emit('input', item);
- },
- },
-};
-</script>
-
-<template>
- <div>
- <div class="js-gcp-machine-type-dropdown dropdown">
- <dropdown-hidden-input :name="fieldName" :value="selectedItemValue" />
- <dropdown-button
- :class="{ 'border-danger': hasErrors }"
- :is-disabled="disabled"
- :is-loading="loading"
- :toggle-text="toggleText"
- />
- <div class="dropdown-menu dropdown-select">
- <dropdown-search-input v-model="searchQuery" :placeholder-text="searchFieldPlaceholder" />
- <div class="dropdown-content">
- <ul>
- <li v-if="!results.length">
- <span class="js-empty-text menu-item">
- {{ emptyText }}
- </span>
- </li>
- <li v-for="item in results" :key="item.id">
- <button class="js-dropdown-item" type="button" @click.prevent="select(item)">
- <slot name="item" :item="item">
- {{ item.name }}
- </slot>
- </button>
- </li>
- </ul>
- </div>
- </div>
- </div>
- <span
- v-if="hasErrors && errorMessage"
- :class="[
- 'form-text js-eks-dropdown-error-message',
- {
- 'text-danger': hasErrors,
- 'text-muted': !hasErrors,
- },
- ]"
- >
- {{ errorMessage }}
- </span>
- </div>
-</template>
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue
index 6e74963dcb0..1ec45c8b651 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue
+++ b/app/assets/javascripts/create_cluster/eks_cluster/components/eks_cluster_configuration_form.vue
@@ -14,12 +14,5 @@ export default {
};
</script>
<template>
- <form name="eks-cluster-configuration-form">
- <div class="form-group">
- <label class="label-bold" name="role" for="eks-role">
- {{ s__('ClusterIntegration|Role name') }}
- </label>
- <role-name-dropdown />
- </div>
- </form>
+ <form name="eks-cluster-configuration-form"></form>
</template>
diff --git a/app/assets/javascripts/create_cluster/eks_cluster/components/role_name_dropdown.vue b/app/assets/javascripts/create_cluster/eks_cluster/components/role_name_dropdown.vue
index 70230b294ac..e69de29bb2d 100644
--- a/app/assets/javascripts/create_cluster/eks_cluster/components/role_name_dropdown.vue
+++ b/app/assets/javascripts/create_cluster/eks_cluster/components/role_name_dropdown.vue
@@ -1,53 +0,0 @@
-<script>
-import { sprintf, s__ } from '~/locale';
-
-import ClusterFormDropdown from './cluster_form_dropdown.vue';
-
-export default {
- components: {
- ClusterFormDropdown,
- },
- props: {
- roles: {
- type: Array,
- required: false,
- default: () => [],
- },
- loading: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- computed: {
- helpText() {
- return sprintf(
- s__(
- 'ClusterIntegration|Select the IAM Role to allow Amazon EKS and the Kubernetes control plane to manage AWS resources on your behalf. To use a new role name, first create one on %{startLink}Amazon Web Services%{endLink}.',
- ),
- {
- startLink:
- '<a href="https://console.aws.amazon.com/iam/home?#roles" target="_blank" rel="noopener noreferrer">',
- endLink: '</a>',
- },
- false,
- );
- },
- },
-};
-</script>
-<template>
- <div>
- <cluster-form-dropdown
- field-id="eks-role-name"
- field-name="eks-role-name"
- :items="roles"
- :loading="loading"
- :loading-text="s__('ClusterIntegration|Loading IAM Roles')"
- :placeholder="s__('ClusterIntergation|Select role name')"
- :search-field-placeholder="s__('ClusterIntegration|Search IAM Roles')"
- :empty-text="s__('ClusterIntegration|No IAM Roles found')"
- />
- <p class="form-text text-muted" v-html="helpText"></p>
- </div>
-</template>
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index f49246cf07b..515402fc506 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -1,4 +1,5 @@
-/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, vars-on-top, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func */
+/* eslint-disable func-names, no-underscore-dangle, no-var, one-var, vars-on-top, no-unused-vars, no-shadow, no-cond-assign, prefer-arrow-callback, no-return-assign, no-else-return, camelcase, no-lonely-if, guard-for-in, no-restricted-syntax, consistent-return, prefer-template, no-param-reassign, no-loop-func */
+/* global fuzzaldrinPlus */
import $ from 'jquery';
import _ from 'underscore';
@@ -65,10 +66,12 @@ GitLabDropdownInput = (function() {
})();
GitLabDropdownFilter = (function() {
- var BLUR_KEYCODES, HAS_VALUE_CLASS;
+ var ARROW_KEY_CODES, BLUR_KEYCODES, HAS_VALUE_CLASS;
BLUR_KEYCODES = [27, 40];
+ ARROW_KEY_CODES = [38, 40];
+
HAS_VALUE_CLASS = 'has-value';
function GitLabDropdownFilter(input, options) {
@@ -874,8 +877,9 @@ GitLabDropdown = (function() {
};
GitLabDropdown.prototype.addArrowKeyEvent = function() {
- var ARROW_KEY_CODES, selector;
+ var $input, ARROW_KEY_CODES, selector;
ARROW_KEY_CODES = [38, 40];
+ $input = this.dropdown.find('.dropdown-input-field');
selector = SELECTABLE_CLASSES;
if (this.dropdown.find('.dropdown-toggle-page').length) {
selector = '.dropdown-page-one ' + selector;
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index a9e086fade8..db4607ca58d 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -1,9 +1,10 @@
-/* eslint-disable no-var, one-var, consistent-return */
+/* eslint-disable no-var, one-var, no-unused-vars, consistent-return */
import $ from 'jquery';
import axios from './lib/utils/axios_utils';
import { addDelimiter } from './lib/utils/text_utility';
import flash from './flash';
+import TaskList from './task_list';
import CreateMergeRequestDropdown from './create_merge_request_dropdown';
import IssuablesHelper from './helpers/issuables_helper';
import { __ } from './locale';
diff --git a/app/assets/javascripts/jobs/components/log/duration_badge.vue b/app/assets/javascripts/jobs/components/log/duration_badge.vue
deleted file mode 100644
index 83f62703d27..00000000000
--- a/app/assets/javascripts/jobs/components/log/duration_badge.vue
+++ /dev/null
@@ -1,13 +0,0 @@
-<script>
-export default {
- props: {
- duration: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-<template>
- <div class="duration rounded align-self-start px-2 ml-2 flex-shrink-0">{{ duration }}</div>
-</template>
diff --git a/app/assets/javascripts/jobs/components/log/line.vue b/app/assets/javascripts/jobs/components/log/line.vue
index 336ae623f0f..86d0fcc3b74 100644
--- a/app/assets/javascripts/jobs/components/log/line.vue
+++ b/app/assets/javascripts/jobs/components/log/line.vue
@@ -21,7 +21,7 @@ export default {
<template>
<div class="line">
<line-number :line-number="line.lineNumber" :path="path" />
- <span v-for="(content, i) in line.content" :key="i" :class="content.style">{{
+ <span v-for="(content, i) in line.content" :key="i" class="line-text" :class="content.style">{{
content.text
}}</span>
</div>
diff --git a/app/assets/javascripts/jobs/components/log/line_header.vue b/app/assets/javascripts/jobs/components/log/line_header.vue
index af8de9ec0fa..4ec212d2333 100644
--- a/app/assets/javascripts/jobs/components/log/line_header.vue
+++ b/app/assets/javascripts/jobs/components/log/line_header.vue
@@ -1,13 +1,11 @@
<script>
import Icon from '~/vue_shared/components/icon.vue';
import LineNumber from './line_number.vue';
-import DurationBadge from './duration_badge.vue';
export default {
components: {
Icon,
LineNumber,
- DurationBadge,
},
props: {
line: {
@@ -22,11 +20,6 @@ export default {
type: String,
required: true,
},
- duration: {
- type: String,
- required: false,
- default: '',
- },
},
computed: {
iconName() {
@@ -42,16 +35,11 @@ export default {
</script>
<template>
- <div
- class="line collapsible-line d-flex justify-content-between"
- role="button"
- @click="handleOnClick"
- >
- <icon :name="iconName" class="arrow position-absolute" />
+ <div class="line collapsible-line" role="button" @click="handleOnClick">
+ <icon :name="iconName" class="arrow" />
<line-number :line-number="line.lineNumber" :path="path" />
<span v-for="(content, i) in line.content" :key="i" class="line-text" :class="content.style">{{
content.text
}}</span>
- <duration-badge v-if="duration" :duration="duration" />
</div>
</template>
diff --git a/app/assets/javascripts/jobs/components/log/line_number.vue b/app/assets/javascripts/jobs/components/log/line_number.vue
index 6c76bef13d3..e06836e2e97 100644
--- a/app/assets/javascripts/jobs/components/log/line_number.vue
+++ b/app/assets/javascripts/jobs/components/log/line_number.vue
@@ -46,10 +46,7 @@ export default {
};
</script>
<template>
- <gl-link
- :id="lineNumberId"
- class="d-inline-block text-right position-absolute line-number"
- :href="buildLineNumber"
- >{{ parsedLineNumber }}</gl-link
- >
+ <gl-link :id="lineNumberId" class="line-number" :href="buildLineNumber">{{
+ parsedLineNumber
+ }}</gl-link>
</template>
diff --git a/app/assets/javascripts/jobs/components/log/log.vue b/app/assets/javascripts/jobs/components/log/log.vue
index 429796aeb4e..5db866afe5a 100644
--- a/app/assets/javascripts/jobs/components/log/log.vue
+++ b/app/assets/javascripts/jobs/components/log/log.vue
@@ -9,7 +9,7 @@ export default {
LogLineHeader,
},
computed: {
- ...mapState(['traceEndpoint', 'trace', 'isTraceComplete']),
+ ...mapState(['traceEndpoint', 'trace']),
},
methods: {
...mapActions(['toggleCollapsibleLine']),
@@ -20,13 +20,12 @@ export default {
};
</script>
<template>
- <code class="job-log d-block">
+ <code class="job-log">
<template v-for="(section, index) in trace">
<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)"
@@ -42,11 +41,5 @@ export default {
</template>
<log-line v-else :key="section.offset" :line="section" :path="traceEndpoint" />
</template>
-
- <div v-if="!isTraceComplete" class="js-log-animation loader-animation pt-3 pl-3">
- <div class="dot"></div>
- <div class="dot"></div>
- <div class="dot"></div>
- </div>
</code>
</template>
diff --git a/app/assets/javascripts/jobs/store/utils.js b/app/assets/javascripts/jobs/store/utils.js
index 261ec90cd12..f6a87b9a212 100644
--- a/app/assets/javascripts/jobs/store/utils.js
+++ b/app/assets/javascripts/jobs/store/utils.js
@@ -1,21 +1,10 @@
/**
- * Adds the line number property
- * @param Object line
- * @param Number lineNumber
- */
-export const parseLine = (line = {}, lineNumber) => ({
- ...line,
- lineNumber,
-});
-
-/**
* Parses the job log content into a structure usable by the template
*
* For collaspible lines (section_header = true):
* - creates a new array to hold the lines that are collpasible,
* - adds a isClosed property to handle toggle
* - adds a isHeader property to handle template logic
- * - adds the section_duration
* For each line:
* - adds the index as lineNumber
*
@@ -25,21 +14,27 @@ export const parseLine = (line = {}, lineNumber) => ({
export const logLinesParser = (lines = [], lineNumberStart) =>
lines.reduce((acc, line, index) => {
const lineNumber = lineNumberStart ? lineNumberStart + index : index;
- const last = acc[acc.length - 1];
-
if (line.section_header) {
acc.push({
isClosed: true,
isHeader: true,
- line: parseLine(line, lineNumber),
+ line: {
+ ...line,
+ lineNumber,
+ },
+
lines: [],
});
- } else if (acc.length && last.isHeader && !line.section_duration && line.content.length) {
- last.lines.push(parseLine(line, lineNumber));
- } else if (acc.length && last.isHeader && line.section_duration) {
- last.section_duration = line.section_duration;
- } else if (line.content.length) {
- acc.push(parseLine(line, lineNumber));
+ } else if (acc.length && acc[acc.length - 1].isHeader) {
+ acc[acc.length - 1].lines.push({
+ ...line,
+ lineNumber,
+ });
+ } else {
+ acc.push({
+ ...line,
+ lineNumber,
+ });
}
return acc;
diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js
index 5dcc719f7c3..7064731a5ea 100644
--- a/app/assets/javascripts/label_manager.js
+++ b/app/assets/javascripts/label_manager.js
@@ -1,4 +1,4 @@
-/* eslint-disable class-methods-use-this, no-underscore-dangle, no-param-reassign, func-names */
+/* eslint-disable class-methods-use-this, no-underscore-dangle, no-param-reassign, no-unused-vars, func-names */
import $ from 'jquery';
import Sortable from 'sortablejs';
@@ -50,7 +50,7 @@ export default class LabelManager {
$(e.currentTarget).tooltip('hide');
}
- toggleEmptyState() {
+ toggleEmptyState($label, $btn, action) {
this.emptyState.classList.toggle(
'hidden',
Boolean(this.prioritizedLabels[0].querySelector(':scope > li')),
@@ -61,6 +61,7 @@ export default class LabelManager {
if (persistState == null) {
persistState = true;
}
+ const _this = this;
const url = $label.find('.js-toggle-priority').data('url');
let $target = this.prioritizedLabels;
let $from = this.otherLabels;
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 8cc3bc8373f..177aa02b8e0 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-useless-return, func-names, no-var, no-underscore-dangle, prefer-arrow-callback, one-var, prefer-template, no-new, consistent-return, object-shorthand, no-shadow, no-param-reassign, vars-on-top, no-lonely-if, no-else-return, dot-notation, no-empty */
+/* eslint-disable no-useless-return, func-names, no-var, no-underscore-dangle, prefer-arrow-callback, one-var, no-unused-vars, prefer-template, no-new, consistent-return, object-shorthand, no-shadow, no-param-reassign, vars-on-top, no-lonely-if, no-else-return, dot-notation, no-empty */
/* global Issuable */
/* global ListLabel */
@@ -26,6 +26,7 @@ export default class LabelsSelect {
$els.each(function(i, dropdown) {
var $block,
+ $colorPreview,
$dropdown,
$form,
$loading,
@@ -34,6 +35,8 @@ export default class LabelsSelect {
$value,
abilityName,
defaultLabel,
+ enableLabelCreateButton,
+ issueURLSplit,
issueUpdateURL,
labelUrl,
namespacePath,
@@ -44,11 +47,16 @@ export default class LabelsSelect {
showNo,
$sidebarLabelTooltip,
initialSelected,
+ $toggleText,
fieldName,
+ useId,
+ propertyName,
showMenuAbove,
+ $container,
$dropdownContainer;
$dropdown = $(dropdown);
$dropdownContainer = $dropdown.closest('.labels-filter');
+ $toggleText = $dropdown.find('.dropdown-toggle-text');
namespacePath = $dropdown.data('namespacePath');
projectPath = $dropdown.data('projectPath');
issueUpdateURL = $dropdown.data('issueUpdate');
@@ -69,6 +77,10 @@ export default class LabelsSelect {
$value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut();
fieldName = $dropdown.data('fieldName');
+ useId = $dropdown.is(
+ '.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown',
+ );
+ propertyName = useId ? 'id' : 'title';
initialSelected = $selectbox
.find('input[name="' + $dropdown.data('fieldName') + '"]')
.map(function() {
@@ -112,7 +124,7 @@ export default class LabelsSelect {
axios
.put(issueUpdateURL, data)
.then(({ data }) => {
- var labelCount, template, labelTooltipTitle, labelTitles;
+ var labelCount, template, labelTooltipTitle, labelTitles, formattedLabels;
$loading.fadeOut();
$dropdown.trigger('loaded.gl.dropdown');
$selectbox.hide();
@@ -234,10 +246,12 @@ export default class LabelsSelect {
renderRow: function(label) {
var linkEl,
listItemEl,
+ color,
colorEl,
indeterminate,
removesAll,
selectedClass,
+ spacing,
i,
marked,
dropdownValue;
@@ -364,7 +378,7 @@ export default class LabelsSelect {
}
},
hidden: function() {
- var isIssueIndex, isMRIndex, page;
+ var isIssueIndex, isMRIndex, page, selectedLabels;
page = $('body').attr('data-page');
isIssueIndex = page === 'projects:issues:index';
isMRIndex = page === 'projects:merge_requests:index';
@@ -381,6 +395,9 @@ export default class LabelsSelect {
}
if ($dropdown.hasClass('js-multiselect')) {
if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
+ selectedLabels = $dropdown
+ .closest('form')
+ .find("input:hidden[name='" + $dropdown.data('fieldName') + "']");
Issuable.filterResults($dropdown.closest('form'));
} else if ($dropdown.hasClass('js-filter-submit')) {
$dropdown.closest('form').submit();
@@ -478,7 +495,7 @@ export default class LabelsSelect {
}
}
},
- opened: function() {
+ opened: function(e) {
if ($dropdown.hasClass('js-issue-board-sidebar')) {
const previousSelection = $dropdown.attr('data-selected');
this.selected = previousSelection ? previousSelection.split(',') : [];
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index 7873eaf059f..b7922e29bb0 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, no-param-reassign, one-var, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, consistent-return */
+/* eslint-disable func-names, no-var, no-param-reassign, one-var, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, consistent-return, no-unused-vars */
import $ from 'jquery';
import { insertText } from '~/lib/utils/common_utils';
@@ -157,7 +157,7 @@ export function insertMarkdownText({
if (tag === LINK_TAG_PATTERN) {
if (URL) {
try {
- new URL(selected); // eslint-disable-line no-new
+ const ignoredUrl = new URL(selected);
// valid url
tag = '[text]({text})';
select = 'text';
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 9c0d55326ee..8f077685b07 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable one-var, object-shorthand, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
+/* eslint-disable one-var, no-unused-vars, object-shorthand, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
/* global Issuable */
/* global ListMilestone */
@@ -37,6 +37,7 @@ export default class MilestoneSelect {
selectedMilestone,
selectedMilestoneDefault;
const $dropdown = $(dropdown);
+ const projectId = $dropdown.data('projectId');
const milestonesUrl = $dropdown.data('milestones');
const issueUpdateURL = $dropdown.data('issueUpdate');
const showNo = $dropdown.data('showNo');
@@ -47,6 +48,7 @@ export default class MilestoneSelect {
const useId = $dropdown.data('useId');
const defaultLabel = $dropdown.data('defaultLabel');
const defaultNo = $dropdown.data('defaultNo');
+ const issuableId = $dropdown.data('issuableId');
const abilityName = $dropdown.data('abilityName');
const $selectBox = $dropdown.closest('.selectbox');
const $block = $selectBox.closest('.block');
@@ -119,7 +121,7 @@ export default class MilestoneSelect {
fields: ['title'],
},
selectable: true,
- toggleLabel: (selected, el) => {
+ toggleLabel: (selected, el, e) => {
if (selected && 'id' in selected && $(el).hasClass('is-active')) {
return selected.title;
} else {
@@ -151,7 +153,7 @@ export default class MilestoneSelect {
},
vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: clickEvent => {
- const { e } = clickEvent;
+ const { $el, e } = clickEvent;
let selected = clickEvent.selectedObj;
let data, modalStoreFilter;
diff --git a/app/assets/javascripts/monitoring/components/charts/column.vue b/app/assets/javascripts/monitoring/components/charts/column.vue
index ee6aaeb7dde..83136d43479 100644
--- a/app/assets/javascripts/monitoring/components/charts/column.vue
+++ b/app/assets/javascripts/monitoring/components/charts/column.vue
@@ -100,7 +100,7 @@ export default {
};
</script>
<template>
- <div class="prometheus-graph">
+ <div class="prometheus-graph col-12 col-lg-6">
<div class="prometheus-graph-header">
<h5 ref="graphTitle" class="prometheus-graph-title">{{ graphData.title }}</h5>
<div ref="graphWidgets" class="prometheus-graph-widgets"><slot></slot></div>
diff --git a/app/assets/javascripts/monitoring/components/charts/empty_chart.vue b/app/assets/javascripts/monitoring/components/charts/empty_chart.vue
index eedc5162e0c..73682adc4ee 100644
--- a/app/assets/javascripts/monitoring/components/charts/empty_chart.vue
+++ b/app/assets/javascripts/monitoring/components/charts/empty_chart.vue
@@ -27,7 +27,7 @@ export default {
};
</script>
<template>
- <div class="prometheus-graph d-flex flex-column justify-content-center">
+ <div class="prometheus-graph col-12 col-lg-6 d-flex flex-column justify-content-center">
<div class="prometheus-graph-header">
<h5 ref="graphTitle" class="prometheus-graph-title">{{ graphTitle }}</h5>
</div>
diff --git a/app/assets/javascripts/monitoring/components/charts/single_stat.vue b/app/assets/javascripts/monitoring/components/charts/single_stat.vue
index 076682820e6..7428b27a9c3 100644
--- a/app/assets/javascripts/monitoring/components/charts/single_stat.vue
+++ b/app/assets/javascripts/monitoring/components/charts/single_stat.vue
@@ -29,7 +29,7 @@ export default {
};
</script>
<template>
- <div class="prometheus-graph">
+ <div class="prometheus-graph col-12 col-lg-6">
<div class="prometheus-graph-header">
<h5 ref="graphTitle" class="prometheus-graph-title">{{ graphTitle }}</h5>
</div>
diff --git a/app/assets/javascripts/monitoring/components/charts/time_series.vue b/app/assets/javascripts/monitoring/components/charts/time_series.vue
index 5f1d742d952..02e7a7ba0a6 100644
--- a/app/assets/javascripts/monitoring/components/charts/time_series.vue
+++ b/app/assets/javascripts/monitoring/components/charts/time_series.vue
@@ -43,6 +43,11 @@ export default {
required: false,
default: '',
},
+ showBorder: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
singleEmbed: {
type: Boolean,
required: false,
@@ -267,66 +272,71 @@ export default {
</script>
<template>
- <div class="prometheus-graph">
- <div class="prometheus-graph-header">
- <h5 class="prometheus-graph-title js-graph-title">{{ graphData.title }}</h5>
- <gl-button
- v-if="exportMetricsToCsvEnabled"
- :href="downloadLink"
- :title="__('Download CSV')"
- :aria-label="__('Download CSV')"
- style="margin-left: 200px;"
- download="chart_metrics.csv"
- >
- {{ __('Download CSV') }}
- </gl-button>
- <div class="prometheus-graph-widgets js-graph-widgets">
- <slot></slot>
+ <div
+ class="prometheus-graph col-12"
+ :class="[showBorder ? 'p-2' : 'p-0', { 'col-lg-6': !singleEmbed }]"
+ >
+ <div :class="{ 'prometheus-graph-embed w-100 p-3': showBorder }">
+ <div class="prometheus-graph-header">
+ <h5 class="prometheus-graph-title js-graph-title">{{ graphData.title }}</h5>
+ <gl-button
+ v-if="exportMetricsToCsvEnabled"
+ :href="downloadLink"
+ :title="__('Download CSV')"
+ :aria-label="__('Download CSV')"
+ style="margin-left: 200px;"
+ download="chart_metrics.csv"
+ >
+ {{ __('Download CSV') }}
+ </gl-button>
+ <div class="prometheus-graph-widgets js-graph-widgets">
+ <slot></slot>
+ </div>
</div>
- </div>
- <component
- :is="glChartComponent"
- ref="chart"
- v-bind="$attrs"
- :data="chartData"
- :option="chartOptions"
- :format-tooltip-text="formatTooltipText"
- :thresholds="thresholds"
- :width="width"
- :height="height"
- @updated="onChartUpdated"
- >
- <template v-if="tooltip.isDeployment">
- <template slot="tooltipTitle">
- {{ __('Deployed') }}
- </template>
- <div slot="tooltipContent" class="d-flex align-items-center">
- <icon name="commit" class="mr-2" />
- <gl-link :href="tooltip.commitUrl">{{ tooltip.sha }}</gl-link>
- </div>
- </template>
- <template v-else>
- <template slot="tooltipTitle">
- <div class="text-nowrap">
- {{ tooltip.title }}
+ <component
+ :is="glChartComponent"
+ ref="chart"
+ v-bind="$attrs"
+ :data="chartData"
+ :option="chartOptions"
+ :format-tooltip-text="formatTooltipText"
+ :thresholds="thresholds"
+ :width="width"
+ :height="height"
+ @updated="onChartUpdated"
+ >
+ <template v-if="tooltip.isDeployment">
+ <template slot="tooltipTitle">
+ {{ __('Deployed') }}
+ </template>
+ <div slot="tooltipContent" class="d-flex align-items-center">
+ <icon name="commit" class="mr-2" />
+ <gl-link :href="tooltip.commitUrl">{{ tooltip.sha }}</gl-link>
</div>
</template>
- <template slot="tooltipContent">
- <div
- v-for="(content, key) in tooltip.content"
- :key="key"
- class="d-flex justify-content-between"
- >
- <gl-chart-series-label :color="isMultiSeries ? content.color : ''">
- {{ content.name }}
- </gl-chart-series-label>
- <div class="prepend-left-32">
- {{ content.value }}
+ <template v-else>
+ <template slot="tooltipTitle">
+ <div class="text-nowrap">
+ {{ tooltip.title }}
</div>
- </div>
+ </template>
+ <template slot="tooltipContent">
+ <div
+ v-for="(content, key) in tooltip.content"
+ :key="key"
+ class="d-flex justify-content-between"
+ >
+ <gl-chart-series-label :color="isMultiSeries ? content.color : ''">
+ {{ content.name }}
+ </gl-chart-series-label>
+ <div class="prepend-left-32">
+ {{ content.value }}
+ </div>
+ </div>
+ </template>
</template>
- </template>
- </component>
+ </component>
+ </div>
</div>
</template>
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 7a5a3789bd6..d330ceb836c 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -456,7 +456,6 @@ export default {
<panel-type
v-for="(graphData, graphIndex) in groupData.metrics"
:key="`panel-type-${graphIndex}`"
- class="col-12 col-lg-6 pb-3"
:clipboard-text="generateLink(groupData.group, graphData.title, graphData.y_label)"
:graph-data="graphData"
:dashboard-width="elWidth"
@@ -469,7 +468,6 @@ export default {
<monitor-time-series-chart
v-for="(graphData, graphIndex) in chartsWithData(groupData.metrics)"
:key="graphIndex"
- class="col-12 col-lg-6 pb-3"
:graph-data="graphData"
:deployment-data="deploymentData"
:thresholds="getGraphAlertValues(graphData.queries)"
diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue
index da1e88071ab..b516a82c170 100644
--- a/app/assets/javascripts/monitoring/components/embed.vue
+++ b/app/assets/javascripts/monitoring/components/embed.vue
@@ -95,7 +95,6 @@ export default {
<monitor-time-series-chart
v-for="graphData in charts"
:key="graphData.title"
- class="w-100"
:graph-data="graphData"
:container-width="elWidth"
group-id="monitor-area-chart"
diff --git a/app/assets/javascripts/network/branch_graph.js b/app/assets/javascripts/network/branch_graph.js
index fcfc2570b3d..d1fa9f5e2a2 100644
--- a/app/assets/javascripts/network/branch_graph.js
+++ b/app/assets/javascripts/network/branch_graph.js
@@ -1,8 +1,9 @@
-/* eslint-disable func-names, no-var, one-var, no-loop-func, consistent-return, prefer-template, prefer-arrow-callback, camelcase */
+/* eslint-disable func-names, no-var, one-var, no-loop-func, consistent-return, no-unused-vars, prefer-template, prefer-arrow-callback, camelcase */
import $ from 'jquery';
import { __ } from '../locale';
import axios from '../lib/utils/axios_utils';
+import flash from '../flash';
import Raphael from './raphael';
export default (function() {
@@ -103,7 +104,7 @@ export default (function() {
};
BranchGraph.prototype.buildGraph = function() {
- var cuday, cumonth, day, len, mm, ref;
+ var cuday, cumonth, day, j, len, mm, ref;
const { r } = this;
cuday = 0;
cumonth = '';
@@ -177,7 +178,7 @@ export default (function() {
return $(element).scroll(
(function(_this) {
- return function() {
+ return function(event) {
return _this.renderPartialGraph();
};
})(this),
@@ -213,7 +214,7 @@ export default (function() {
};
BranchGraph.prototype.appendLabel = function(x, y, commit) {
- var label, rect, shortrefs, text, textbox;
+ var label, rect, shortrefs, text, textbox, triangle;
if (!commit.refs) {
return;
@@ -238,8 +239,7 @@ export default (function() {
'fill-opacity': 0.5,
stroke: 'none',
});
- // Generate the triangle right of the tag box
- r.path(['M', x - 5, y, 'L', x - 15, y - 4, 'L', x - 15, y + 4, 'Z']).attr({
+ triangle = r.path(['M', x - 5, y, 'L', x - 15, y - 4, 'L', x - 15, y + 4, 'Z']).attr({
fill: '#000',
'fill-opacity': 0.5,
stroke: 'none',
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 9cc56b34c75..9cc31e26648 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -2,9 +2,10 @@
no-unused-expressions, one-var, default-case,
prefer-template, consistent-return, no-alert, no-return-assign,
no-param-reassign, prefer-arrow-callback, no-else-return, vars-on-top,
-no-shadow, no-useless-escape, class-methods-use-this */
+no-unused-vars, no-shadow, no-useless-escape, class-methods-use-this */
/* global ResolveService */
+/* global mrRefreshWidgetUrl */
/*
old_notes_spec.js is the spec for the legacy, jQuery notes application. It has nothing to do with the new, fancy Vue notes app.
@@ -36,6 +37,7 @@ import {
isMetaKey,
isInMRPage,
} from './lib/utils/common_utils';
+import imageDiffHelper from './image_diff/helpers/index';
import { localTimeAgo } from './lib/utils/datetime_utility';
import { sprintf, s__, __ } from './locale';
@@ -681,7 +683,7 @@ export default class Notes {
);
}
- updateNoteError() {
+ updateNoteError($parentTimeline) {
// eslint-disable-next-line no-new
new Flash(
__('Your comment could not be updated! Please check your network connection and try again.'),
@@ -695,6 +697,7 @@ export default class Notes {
*/
addDiscussionNote($form, note, isNewDiffComment) {
if ($form.attr('data-resolve-all') != null) {
+ var projectPath = $form.data('projectPath');
var discussionId = $form.data('discussionId');
var mergeRequestId = $form.data('noteableIid');
@@ -743,6 +746,7 @@ export default class Notes {
if (currentContent === initialContent) {
this.removeNoteEditForm($el);
} else {
+ var $buttons = $el.find('.note-form-actions');
var isWidgetVisible = isInViewport($el.get(0));
if (!isWidgetVisible) {
@@ -762,7 +766,7 @@ export default class Notes {
* Replaces the note text with the note edit form
* Adds a data attribute to the form with the original content of the note for cancellations
*/
- showEditForm(e) {
+ showEditForm(e, scrollTo, myLastNote) {
e.preventDefault();
var $target = $(e.target);
@@ -846,11 +850,16 @@ export default class Notes {
* Removes the whole discussion if the last note is being removed.
*/
removeNote(e) {
- var noteElId, $note;
+ var noteElId, noteId, dataNoteId, $note, lineHolder;
$note = $(e.currentTarget).closest('.note');
noteElId = $note.attr('id');
+ noteId = $note.attr('data-note-id');
+ lineHolder = $(e.currentTarget)
+ .closest('.notes[data-discussion-id]')
+ .closest('.notes_holder')
+ .prev('.line_holder');
$(`.note[id="${noteElId}"]`).each(
- (function() {
+ (function(_this) {
// A same note appears in the "Discussion" and in the "Changes" tab, we have
// to remove all. Using $('.note[id='noteId']') ensure we get all the notes,
// where $('#noteId') would return only one.
@@ -1055,8 +1064,25 @@ export default class Notes {
this.setupDiscussionNoteForm($link, newForm);
}
- toggleDiffNote({ target, lineType, forceShow, showReplyInput = false }) {
- var $link, addForm, hasNotes, newForm, noteForm, replyButton, row, rowCssToAdd;
+ toggleDiffNote({
+ target,
+ lineType,
+ forceShow,
+ showReplyInput = false,
+ currentUsername,
+ currentUserAvatar,
+ currentUserFullname,
+ }) {
+ var $link,
+ addForm,
+ hasNotes,
+ newForm,
+ noteForm,
+ replyButton,
+ row,
+ rowCssToAdd,
+ targetContent,
+ isDiffCommentAvatar;
$link = $(target);
row = $link.closest('tr');
const nextRow = row.next();
@@ -1489,7 +1515,7 @@ export default class Notes {
let tempFormContent;
// Identify executed quick actions from `formContent`
- const executedCommands = availableQuickActions.filter(command => {
+ const executedCommands = availableQuickActions.filter((command, index) => {
const commandRegex = new RegExp(`/${command.name}`);
return commandRegex.test(formContent);
});
@@ -1814,6 +1840,8 @@ export default class Notes {
const $noteBody = $editingNote.find('.js-task-list-container');
const $noteBodyText = $noteBody.find('.note-text');
const { formData, formContent, formAction } = this.getFormData($form);
+ const $diffFile = $form.closest('.diff-file');
+ const $notesContainer = $form.closest('.notes');
// Cache original comment content
const cachedNoteBodyText = $noteBodyText.html();
diff --git a/app/assets/javascripts/notes/components/discussion_actions.vue b/app/assets/javascripts/notes/components/discussion_actions.vue
index e3be91a4966..6bbf2fa6ee4 100644
--- a/app/assets/javascripts/notes/components/discussion_actions.vue
+++ b/app/assets/javascripts/notes/components/discussion_actions.vue
@@ -36,10 +36,11 @@ export default {
},
},
computed: {
+ resolvableNotes() {
+ return this.discussion.notes.filter(x => x.resolvable);
+ },
userCanResolveDiscussion() {
- return this.discussion.notes.every(
- note => note.current_user && note.current_user.can_resolve,
- );
+ return this.resolvableNotes.every(note => note.current_user && note.current_user.can_resolve);
},
},
};
diff --git a/app/assets/javascripts/pages/admin/application_settings/general/index.js b/app/assets/javascripts/pages/admin/application_settings/show/index.js
index 5ec9688a6e4..5ec9688a6e4 100644
--- a/app/assets/javascripts/pages/admin/application_settings/general/index.js
+++ b/app/assets/javascripts/pages/admin/application_settings/show/index.js
diff --git a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
index ec3919dd073..988ae164955 100644
--- a/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, object-shorthand, no-var, one-var, camelcase, no-param-reassign, no-return-assign, prefer-arrow-callback, consistent-return, no-cond-assign, no-else-return */
+/* eslint-disable func-names, object-shorthand, no-var, one-var, camelcase, no-param-reassign, no-return-assign, prefer-arrow-callback, consistent-return, no-unused-vars, no-cond-assign, no-else-return */
import _ from 'underscore';
export default {
@@ -126,7 +126,7 @@ export default {
_.each(
_.omit(log_entry, 'author_name', 'author_email'),
(function(_this) {
- return function(value) {
+ return function(value, key) {
if (_this.in_range(value.date, date_range)) {
parsed_entry.dates[value.date] = value[field];
parsed_entry.commits += value.commits;
diff --git a/app/assets/javascripts/performance_bar/services/performance_bar_service.js b/app/assets/javascripts/performance_bar/services/performance_bar_service.js
index 61b35b4b8f5..d8c23c82f7f 100644
--- a/app/assets/javascripts/performance_bar/services/performance_bar_service.js
+++ b/app/assets/javascripts/performance_bar/services/performance_bar_service.js
@@ -1,6 +1,10 @@
+import Vue from 'vue';
+import _ from 'underscore';
import axios from '../../lib/utils/axios_utils';
import { parseBoolean } from '~/lib/utils/common_utils';
+let vueResourceInterceptor;
+
export default class PerformanceBarService {
static fetchRequestDetails(peekUrl, requestId) {
return axios.get(peekUrl, { params: { request_id: requestId } });
@@ -20,11 +24,16 @@ export default class PerformanceBarService {
return response;
};
+ vueResourceInterceptor = (request, next) => next(interceptor);
+
+ Vue.http.interceptors.push(vueResourceInterceptor);
+
return axios.interceptors.response.use(interceptor);
}
static removeInterceptor(interceptor) {
axios.interceptors.response.eject(interceptor);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, vueResourceInterceptor);
}
static callbackParams(response, peekUrl) {
diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js
index 6a07ccc7586..befe91c332f 100644
--- a/app/assets/javascripts/profile/gl_crop.js
+++ b/app/assets/javascripts/profile/gl_crop.js
@@ -1,10 +1,10 @@
-/* eslint-disable no-useless-escape, no-var, no-underscore-dangle, func-names, no-return-assign, object-shorthand, one-var, consistent-return, class-methods-use-this */
+/* eslint-disable no-useless-escape, no-var, no-underscore-dangle, func-names, no-unused-vars, no-return-assign, object-shorthand, one-var, consistent-return, class-methods-use-this */
import $ from 'jquery';
import 'cropper';
import _ from 'underscore';
-(() => {
+(global => {
// Matches everything but the file name
const FILENAMEREGEX = /^.*[\\\/]/;
@@ -69,7 +69,7 @@ import _ from 'underscore';
this.modalCrop.on('shown.bs.modal', this.onModalShow);
this.modalCrop.on('hidden.bs.modal', this.onModalHide);
this.uploadImageBtn.on('click', this.onUploadImageBtnClick);
- this.cropActionsBtn.on('click', function() {
+ this.cropActionsBtn.on('click', function(e) {
var btn;
btn = this;
return _this.onActionBtnClick(btn);
@@ -128,10 +128,10 @@ import _ from 'underscore';
}
onActionBtnClick(btn) {
- var data;
+ var data, result;
data = $(btn).data();
if (this.modalCropImg.data('cropper') && data.method) {
- return this.modalCropImg.cropper(data.method, data.option);
+ return (result = this.modalCropImg.cropper(data.method, data.option));
}
}
@@ -151,11 +151,12 @@ import _ from 'underscore';
}
dataURLtoBlob(dataURL) {
- var array, binary, i, len;
+ var array, binary, i, len, v;
binary = atob(dataURL.split(',')[1]);
array = [];
for (i = 0, len = binary.length; i < len; i += 1) {
+ v = binary[i];
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], {
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index e73a828c0ae..765cb868f80 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, consistent-return, one-var, no-cond-assign, prefer-template, no-return-assign */
+/* eslint-disable func-names, no-var, consistent-return, one-var, no-cond-assign, prefer-template, no-unused-vars, no-return-assign */
import $ from 'jquery';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
@@ -8,8 +8,9 @@ import { __ } from '~/locale';
// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
const highlighter = function(element, text, matches) {
- var j, lastIndex, len, matchIndex, matchedChars, unmatched;
+ var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched;
lastIndex = 0;
+ highlightText = '';
matchedChars = [];
for (j = 0, len = matches.length; j < len; j += 1) {
matchIndex = matches[j];
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 0cc7a22325b..40a2158de78 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, no-var, consistent-return, one-var, prefer-template, no-else-return, no-param-reassign */
+/* eslint-disable func-names, no-var, no-unused-vars, consistent-return, one-var, prefer-template, no-else-return, no-param-reassign */
import $ from 'jquery';
import _ from 'underscore';
@@ -7,7 +7,7 @@ import flash from './flash';
import axios from './lib/utils/axios_utils';
import { sprintf, s__, __ } from './locale';
-function Sidebar() {
+function Sidebar(currentUser) {
this.toggleTodo = this.toggleTodo.bind(this);
this.sidebar = $('aside');
@@ -15,9 +15,9 @@ function Sidebar() {
this.addEventListeners();
}
-Sidebar.initialize = function() {
+Sidebar.initialize = function(currentUser) {
if (!this.instance) {
- this.instance = new Sidebar();
+ this.instance = new Sidebar(currentUser);
}
};
@@ -77,7 +77,7 @@ Sidebar.prototype.sidebarToggleClicked = function(e, triggered) {
};
Sidebar.prototype.toggleTodo = function(e) {
- var $this, ajaxType, url;
+ var $btnText, $this, $todoLoading, ajaxType, url;
$this = $(e.currentTarget);
ajaxType = $this.data('deletePath') ? 'delete' : 'post';
@@ -140,7 +140,7 @@ Sidebar.prototype.todoUpdateDone = function(data) {
});
};
-Sidebar.prototype.sidebarDropdownLoading = function() {
+Sidebar.prototype.sidebarDropdownLoading = function(e) {
var $loading, $sidebarCollapsedIcon, i, img;
$sidebarCollapsedIcon = $(this)
.closest('.block')
@@ -157,7 +157,7 @@ Sidebar.prototype.sidebarDropdownLoading = function() {
}
};
-Sidebar.prototype.sidebarDropdownLoaded = function() {
+Sidebar.prototype.sidebarDropdownLoaded = function(e) {
var $sidebarCollapsedIcon, i, img;
$sidebarCollapsedIcon = $(this)
.closest('.block')
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index f02c55c3d5b..510a2441924 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -1,10 +1,11 @@
-/* eslint-disable no-return-assign, one-var, no-var, consistent-return, object-shorthand, prefer-template, class-methods-use-this, no-lonely-if, vars-on-top */
+/* eslint-disable no-return-assign, one-var, no-var, no-unused-vars, consistent-return, object-shorthand, prefer-template, class-methods-use-this, no-lonely-if, vars-on-top */
import $ from 'jquery';
import { escape, throttle } from 'underscore';
-import { s__, __ } from '~/locale';
+import { s__, __, sprintf } from '~/locale';
import { getIdenticonBackgroundClass, getIdenticonTitle } from '~/helpers/avatar_helper';
import axios from './lib/utils/axios_utils';
+import DropdownUtils from './filtered_search/dropdown_utils';
import {
isInGroupsPage,
isInProjectPage,
@@ -141,7 +142,7 @@ export class SearchAutocomplete {
});
}
- getSearchText(selectedObject) {
+ getSearchText(selectedObject, el) {
return selectedObject.id ? selectedObject.text : '';
}
@@ -401,7 +402,7 @@ export class SearchAutocomplete {
return this.searchInput.val('').focus();
}
- onSearchInputBlur() {
+ onSearchInputBlur(e) {
this.isFocused = false;
this.wrap.removeClass('search-active');
// If input is blank then restore state
diff --git a/app/assets/javascripts/test_utils/simulate_drag.js b/app/assets/javascripts/test_utils/simulate_drag.js
index f4090de3f1e..c9bf234fcce 100644
--- a/app/assets/javascripts/test_utils/simulate_drag.js
+++ b/app/assets/javascripts/test_utils/simulate_drag.js
@@ -2,8 +2,8 @@ function simulateEvent(el, type, options = {}) {
let event;
if (!el) return null;
- if (/^(pointer|mouse)/.test(type)) {
- event = el.ownerDocument.createEvent('MouseEvent');
+ if (/^mouse/.test(type)) {
+ event = el.ownerDocument.createEvent('MouseEvents');
event.initMouseEvent(
type,
true,
@@ -125,7 +125,7 @@ export default function simulateDrag(options) {
const startTime = new Date().getTime();
const duration = options.duration || 1000;
- simulateEvent(fromEl, 'pointerdown', {
+ simulateEvent(fromEl, 'mousedown', {
button: 0,
clientX: fromRect.cx,
clientY: fromRect.cy,
@@ -146,7 +146,7 @@ export default function simulateDrag(options) {
const y = fromRect.cy + (toRect.cy - fromRect.cy) * progress;
const overEl = fromEl.ownerDocument.elementFromPoint(x, y);
- simulateEvent(overEl, 'pointermove', {
+ simulateEvent(overEl, 'mousemove', {
clientX: x,
clientY: y,
});
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 4b3c42ae848..57efde7f027 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, one-var, no-var, prefer-rest-params, vars-on-top, prefer-arrow-callback, consistent-return, object-shorthand, no-shadow, no-else-return, no-self-compare, prefer-template, no-unused-expressions, yoda, prefer-spread, camelcase, no-param-reassign */
+/* eslint-disable func-names, one-var, no-var, prefer-rest-params, vars-on-top, prefer-arrow-callback, consistent-return, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, yoda, prefer-spread, camelcase, no-param-reassign */
/* global Issuable */
/* global emitSidebarEvent */
@@ -405,7 +405,7 @@ function UsersSelect(currentUser, els, options = {}) {
}
},
defaultLabel: defaultLabel,
- hidden: function() {
+ hidden: function(e) {
if ($dropdown.hasClass('js-multiselect')) {
emitSidebarEvent('sidebar.saveAssignees');
}
@@ -442,6 +442,7 @@ function UsersSelect(currentUser, els, options = {}) {
if (user.beforeDivider && user.name.toLowerCase() === 'unassigned') {
// Unassigned selected
previouslySelected.each((index, element) => {
+ const id = parseInt(element.value, 10);
element.remove();
});
emitSidebarEvent('sidebar.removeAllAssignees');
@@ -547,7 +548,7 @@ function UsersSelect(currentUser, els, options = {}) {
},
updateLabel: $dropdown.data('dropdownTitle'),
renderRow: function(user) {
- var avatar, img, username;
+ var avatar, img, listClosingTags, listWithName, listWithUserName, username;
username = user.username ? '@' + user.username : '';
avatar = user.avatar_url ? user.avatar_url : gon.default_avatar_url;
diff --git a/app/assets/javascripts/zen_mode.js b/app/assets/javascripts/zen_mode.js
index 5438572eadf..e98c4d7bf7a 100644
--- a/app/assets/javascripts/zen_mode.js
+++ b/app/assets/javascripts/zen_mode.js
@@ -1,4 +1,4 @@
-/* eslint-disable func-names, prefer-arrow-callback, consistent-return, camelcase, class-methods-use-this */
+/* eslint-disable func-names, prefer-arrow-callback, no-unused-vars, consistent-return, camelcase, class-methods-use-this */
// Zen Mode (full screen) textarea
//
@@ -62,7 +62,7 @@ export default class ZenMode {
$(document).on(
'zen_mode:leave',
(function(_this) {
- return function() {
+ return function(e) {
return _this.exit();
};
})(this),
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index af05d069f97..7e7b08797b2 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -131,16 +131,16 @@ $notification-box-shadow-color: rgba(0, 0, 0, 0.25);
}
}
-.with-system-header .flash-container {
+.with-system-header .flash-container.sticky {
top: $flash-container-top + $system-header-height;
}
.with-performance-bar {
- .flash-container {
+ .flash-container.sticky {
top: $flash-container-top + $performance-bar-height;
}
- &.with-system-header .flash-container {
+ &.with-system-header .flash-container.sticky {
top: $flash-container-top + $performance-bar-height + $system-header-height;
}
}
diff --git a/app/assets/stylesheets/framework/job_log.scss b/app/assets/stylesheets/framework/job_log.scss
deleted file mode 100644
index 5c2491c8233..00000000000
--- a/app/assets/stylesheets/framework/job_log.scss
+++ /dev/null
@@ -1,49 +0,0 @@
-.job-log {
- font-family: $monospace-font;
- padding: $gl-padding-8 $input-horizontal-padding;
- margin: 0 0 $gl-padding-8;
- font-size: 13px;
- word-break: break-all;
- word-wrap: break-word;
- color: $gl-text-color-inverted;
- border-radius: $border-radius-small;
- min-height: 42px;
- background-color: $builds-trace-bg;
-}
-
-.line {
- padding: 1px $gl-padding 1px $job-log-line-padding;
-}
-
-.line-number {
- color: $gl-text-color-inverted;
- padding: 0 $gl-padding-8;
- min-width: $job-line-number-width;
- margin-left: -$job-line-number-width;
- padding-right: 1em;
-
- &:hover,
- &:active,
- &:visited {
- text-decoration: underline;
- color: $gl-text-color-inverted;
- }
-}
-
-.collapsible-line {
- &:hover {
- background-color: rgba($white-light, 0.2);
- }
-
- .arrow {
- margin-left: -$job-arrow-margin;
- }
-}
-
-.duration {
- background: $gl-gray-400;
-}
-
-.loader-animation {
- @include build-loader-animation;
-}
diff --git a/app/assets/stylesheets/framework/snippets.scss b/app/assets/stylesheets/framework/snippets.scss
index f57b1d9f351..3ab83f4c8e6 100644
--- a/app/assets/stylesheets/framework/snippets.scss
+++ b/app/assets/stylesheets/framework/snippets.scss
@@ -39,6 +39,10 @@
min-height: $header-height;
}
+.snippet-edited-ago {
+ color: $gray-darkest;
+}
+
.snippet-actions {
@include media-breakpoint-up(sm) {
float: right;
diff --git a/app/assets/stylesheets/framework/sortable.scss b/app/assets/stylesheets/framework/sortable.scss
index 25868061d04..8c070200135 100644
--- a/app/assets/stylesheets/framework/sortable.scss
+++ b/app/assets/stylesheets/framework/sortable.scss
@@ -90,21 +90,3 @@
padding: 0;
}
}
-
-.is-dragging {
- // Important because plugin sets inline CSS
- opacity: 1 !important;
-
- * {
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
- user-select: none;
- // !important to make sure no style can override this when dragging
- cursor: grabbing !important;
- }
-
- &.no-drop * {
- cursor: no-drop !important;
- }
-}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index e77527ac130..faa0a9909d5 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -606,9 +606,6 @@ $blame-blue: #254e77;
*/
$builds-trace-bg: #111;
$job-log-highlight-height: 18px;
-$job-log-line-padding: 62px;
-$job-line-number-width: 40px;
-$job-arrow-margin: 50px;
/*
* Commit Page
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 4bf0abccd00..e77a2d1e333 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -2,6 +2,20 @@
cursor: grab;
}
+.is-dragging {
+ // Important because plugin sets inline CSS
+ opacity: 1 !important;
+
+ * {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ // !important to make sure no style can override this when dragging
+ cursor: grabbing !important;
+ }
+}
+
.is-ghost {
opacity: 0.3;
pointer-events: none;
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index a37cbda8558..8359a60ec9f 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -29,6 +29,10 @@
.author-link {
display: inline-block;
}
+
+ .issuable-comments {
+ height: 18px;
+ }
}
.icon-merge-request-unmerged {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index e6feded1d4f..c8d155706a9 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -461,6 +461,10 @@
.author-link {
display: inline-block;
}
+
+ .issuable-comments {
+ height: 18px;
+ }
}
.merge-request-title {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 801e9e7204c..c80beceae52 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -889,7 +889,11 @@ pre.light-well {
@include basic-list-stats;
display: flex;
align-items: center;
- padding: $gl-padding-12 0;
+ padding: $gl-padding 0;
+
+ @include media-breakpoint-up(lg) {
+ padding: $gl-padding 0;
+ }
&.no-description {
@include media-breakpoint-up(sm) {
@@ -905,7 +909,7 @@ pre.light-well {
}
h2 {
- font-size: $gl-font-size;
+ font-size: $gl-font-size-large;
font-weight: $gl-font-weight-bold;
margin-bottom: 0;
@@ -947,7 +951,6 @@ pre.light-well {
.description {
line-height: 1.5;
- color: $gl-text-color-secondary;
}
@include media-breakpoint-down(md) {
@@ -1093,6 +1096,7 @@ pre.light-well {
&:not(.explore) {
.forks {
display: none;
+
}
}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 03e935fceae..dc16ad80980 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -6,16 +6,15 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
before_action :set_application_setting
before_action :whitelist_query_limiting, only: [:usage_data]
- VALID_SETTING_PANELS = %w(general integrations repository templates
+ VALID_SETTING_PANELS = %w(show integrations repository templates
ci_cd reporting metrics_and_profiling
network geo preferences).freeze
- VALID_SETTING_PANELS.each do |action|
- define_method(action) { perform_update if submitted? }
+ def show
end
- def show
- render :general
+ (VALID_SETTING_PANELS - %w(show)).each do |action|
+ define_method(action) { perform_update if submitted? }
end
def update
@@ -145,7 +144,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
def render_update_error
- action = VALID_SETTING_PANELS.include?(action_name) ? action_name : :general
+ action = VALID_SETTING_PANELS.include?(action_name) ? action_name : :show
render action
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index af87e04f9c8..0d992bb35f8 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -211,7 +211,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end
def discussions
- merge_request.discussions_diffs.load_highlight
+ merge_request.preload_discussions_diff_highlight
super
end
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 477ba36e9d1..28c25dbc1e6 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -8,9 +8,6 @@ class Projects::PipelinesController < Projects::ApplicationController
before_action :authorize_read_build!, only: [:index]
before_action :authorize_create_pipeline!, only: [:new, :create]
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
- before_action do
- push_frontend_feature_flag(:hide_dismissed_vulnerabilities)
- end
around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
@@ -199,7 +196,12 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def latest_pipeline
- @project.latest_pipeline_for_ref(params['ref'])
+ ref = params['ref'].presence || @project.default_branch
+ sha = @project.commit(ref)&.sha
+
+ @project.ci_pipelines
+ .newest_first(ref: ref, sha: sha)
+ .first
&.present(current_user: current_user)
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 2364777cdc5..8ed6ff56e2b 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -46,13 +46,10 @@ class IssuableFinder
# This is used in unassigning users
NONE = '0'
- NEGATABLE_PARAMS_HELPER_KEYS = %i[include_subgroups in].freeze
-
attr_accessor :current_user, :params
- class << self
- def scalar_params
- @scalar_params ||= %i[
+ def self.scalar_params
+ @scalar_params ||= %i[
assignee_id
assignee_username
author_id
@@ -63,30 +60,14 @@ class IssuableFinder
search
in
]
- end
-
- def array_params
- @array_params ||= { label_name: [], assignee_username: [] }
- end
-
- # This should not be used in controller strong params!
- def negatable_scalar_params
- @negatable_scalar_params ||= scalar_params + %i[project_id group_id]
- end
-
- # This should not be used in controller strong params!
- def negatable_array_params
- @negatable_array_params ||= array_params.keys.append(:iids)
- end
+ end
- # This should not be used in controller strong params!
- def negatable_params
- @negatable_params ||= negatable_scalar_params + negatable_array_params
- end
+ def self.array_params
+ @array_params ||= { label_name: [], assignee_username: [] }
+ end
- def valid_params
- @valid_params ||= scalar_params + [array_params] + [{ not: [] }]
- end
+ def self.valid_params
+ @valid_params ||= scalar_params + [array_params]
end
def initialize(current_user, params = {})
@@ -98,9 +79,6 @@ class IssuableFinder
items = init_collection
items = filter_items(items)
- # Let's see if we have to negate anything
- items = by_negation(items)
-
# This has to be last as we use a CTE as an optimization fence
# for counts by passing the force_cte param and enabling the
# attempt_group_search_optimizations feature flag
@@ -388,33 +366,6 @@ class IssuableFinder
Array(value).last.to_sym
end
- # Negates all params found in `negatable_params`
- # rubocop: disable CodeReuse/ActiveRecord
- def by_negation(items)
- not_params = params[:not].dup
- # API endpoints send in `nil` values so we test if there are any non-nil
- return items unless not_params.present? && not_params.values.any?
-
- not_params.keep_if { |_k, v| v.present? }.each do |(key, value)|
- # These aren't negatable params themselves, but rather help other searches, so we skip them.
- # They will be added into all the NOT searches.
- next if NEGATABLE_PARAMS_HELPER_KEYS.include?(key.to_sym)
- next unless self.class.negatable_params.include?(key.to_sym)
-
- # These are "helper" params that are required inside the NOT to get the right results. They usually come in
- # at the top-level params, but if they do come in inside the `:not` params, they should take precedence.
- not_helpers = params.slice(*NEGATABLE_PARAMS_HELPER_KEYS).merge(params[:not].slice(*NEGATABLE_PARAMS_HELPER_KEYS))
- not_param = { key => value }.with_indifferent_access.merge(not_helpers)
-
- items_to_negate = self.class.new(current_user, not_param).execute
-
- items = items.where.not(id: items_to_negate)
- end
-
- items
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
# rubocop: disable CodeReuse/ActiveRecord
def by_scope(items)
return items.none if current_user_related? && !current_user
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index 4e71a7a9ead..b50186c5a82 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -11,32 +11,31 @@ module Resolvers
description: 'The list of IIDs of issues, e.g., [1, 2]'
argument :state, Types::IssuableStateEnum,
required: false,
- description: 'Current state of Issue'
+ description: "Current state of Issue"
argument :label_name, GraphQL::STRING_TYPE.to_list_type,
required: false,
- description: 'Labels applied to the Issue'
+ description: "Labels applied to the Issue"
argument :created_before, Types::TimeType,
required: false,
- description: 'Issues created before this date'
+ description: "Issues created before this date"
argument :created_after, Types::TimeType,
required: false,
- description: 'Issues created after this date'
+ description: "Issues created after this date"
argument :updated_before, Types::TimeType,
required: false,
- description: 'Issues updated before this date'
+ description: "Issues updated before this date"
argument :updated_after, Types::TimeType,
required: false,
- description: 'Issues updated after this date'
+ description: "Issues updated after this date"
argument :closed_before, Types::TimeType,
required: false,
- description: 'Issues closed before this date'
+ description: "Issues closed before this date"
argument :closed_after, Types::TimeType,
required: false,
- description: 'Issues closed after this date'
+ description: "Issues closed after this date"
argument :search, GraphQL::STRING_TYPE, # rubocop:disable Graphql/Descriptions
required: false
- argument :sort, Types::SortEnum,
- description: 'Sort issues by this criteria',
+ argument :sort, Types::Sort, # rubocop:disable Graphql/Descriptions
required: false,
default_value: 'created_desc'
diff --git a/app/graphql/types/order.rb b/app/graphql/types/order.rb
new file mode 100644
index 00000000000..c5e1cc406b4
--- /dev/null
+++ b/app/graphql/types/order.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module Types
+ class Types::Order < Types::BaseEnum
+ value "id", "Created at date"
+ value "updated_at", "Updated at date"
+ end
+end
diff --git a/app/graphql/types/sort.rb b/app/graphql/types/sort.rb
new file mode 100644
index 00000000000..1f756fdab69
--- /dev/null
+++ b/app/graphql/types/sort.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Types
+ class Types::Sort < Types::BaseEnum
+ value "updated_desc", "Updated at descending order"
+ value "updated_asc", "Updated at ascending order"
+ value "created_desc", "Created at descending order"
+ value "created_asc", "Created at ascending order"
+ end
+end
diff --git a/app/graphql/types/sort_enum.rb b/app/graphql/types/sort_enum.rb
deleted file mode 100644
index 3245cb33e0d..00000000000
--- a/app/graphql/types/sort_enum.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-# frozen_string_literal: true
-
-module Types
- class SortEnum < BaseEnum
- graphql_name 'Sort'
- description 'Common sort values'
-
- value 'updated_desc', 'Updated at descending order'
- value 'updated_asc', 'Updated at ascending order'
- value 'created_desc', 'Created at descending order'
- value 'created_asc', 'Created at ascending order'
- end
-end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index 46e2c9ce56e..ec1d8577f36 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# frozen_string_literal: true
module PageLayoutHelper
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 5ed95311767..3fb39a19cf0 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -562,7 +562,7 @@ module ProjectsHelper
allowedVisibilityOptions: project_allowed_visibility_levels(project),
visibilityHelpPath: help_page_path('public_access/public_access'),
registryAvailable: Gitlab.config.registry.enabled,
- registryHelpPath: help_page_path('user/packages/container_registry/index'),
+ registryHelpPath: help_page_path('user/project/container_registry'),
lfsAvailable: Gitlab.config.lfs.enabled,
lfsHelpPath: help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs'),
pagesAvailable: Gitlab.config.pages.enabled,
diff --git a/app/models/ci/artifact_blob.rb b/app/models/ci/artifact_blob.rb
index ef00ad75683..d87d6a5cb2f 100644
--- a/app/models/ci/artifact_blob.rb
+++ b/app/models/ci/artifact_blob.rb
@@ -4,7 +4,7 @@ module Ci
class ArtifactBlob
include BlobLike
- EXTENSIONS_SERVED_BY_PAGES = %w[.html .htm .txt .json .xml .log].freeze
+ EXTENSIONS_SERVED_BY_PAGES = %w[.html .htm .txt .json .log].freeze
attr_reader :entry
diff --git a/app/models/ci/build_metadata.rb b/app/models/ci/build_metadata.rb
index 3097e40dd3b..4046048be1c 100644
--- a/app/models/ci/build_metadata.rb
+++ b/app/models/ci/build_metadata.rb
@@ -4,12 +4,9 @@ module Ci
# The purpose of this class is to store Build related data that can be disposed.
# Data that should be persisted forever, should be stored with Ci::Build model.
class BuildMetadata < ApplicationRecord
- BuildTimeout = Struct.new(:value, :source)
-
extend Gitlab::Ci::Model
include Presentable
include ChronicDurationAttribute
- include Gitlab::Utils::StrongMemoize
self.table_name = 'ci_builds_metadata'
@@ -31,16 +28,17 @@ module Ci
enum timeout_source: {
unknown_timeout_source: 1,
project_timeout_source: 2,
- runner_timeout_source: 3,
- job_timeout_source: 4
+ runner_timeout_source: 3
}
def update_timeout_state
- timeout = timeout_with_highest_precedence
+ return unless build.runner.present?
- return unless timeout
+ project_timeout = project&.build_timeout
+ timeout = [project_timeout, build.runner.maximum_timeout].compact.min
+ timeout_source = timeout < project_timeout ? :runner_timeout_source : :project_timeout_source
- update(timeout: timeout.value, timeout_source: timeout.source)
+ update(timeout: timeout, timeout_source: timeout_source)
end
private
@@ -48,37 +46,5 @@ module Ci
def set_build_project
self.project_id ||= self.build.project_id
end
-
- def timeout_with_highest_precedence
- [(job_timeout || project_timeout), runner_timeout].compact.min_by { |timeout| timeout.value }
- end
-
- def project_timeout
- strong_memoize(:project_timeout) do
- BuildTimeout.new(project&.build_timeout, :project_timeout_source)
- end
- end
-
- def job_timeout
- return unless build.options
-
- strong_memoize(:job_timeout) do
- if timeout_from_options = build.options[:job_timeout]
- BuildTimeout.new(timeout_from_options, :job_timeout_source)
- end
- end
- end
-
- def runner_timeout
- return unless runner_timeout_set?
-
- strong_memoize(:runner_timeout) do
- BuildTimeout.new(build.runner.maximum_timeout, :runner_timeout_source)
- end
- end
-
- def runner_timeout_set?
- build.runner&.maximum_timeout.to_i > 0
- end
end
end
diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb
index 50def3ba38c..44c66f06059 100644
--- a/app/models/clusters/applications/ingress.rb
+++ b/app/models/clusters/applications/ingress.rb
@@ -35,6 +35,10 @@ module Clusters
'stable/nginx-ingress'
end
+ def values
+ content_values.to_yaml
+ end
+
def allowed_to_uninstall?
external_ip_or_hostname? && application_jupyter_nil_or_installable?
end
@@ -67,6 +71,23 @@ module Clusters
private
+ def specification
+ return {} unless Feature.enabled?(:ingress_modsecurity)
+
+ {
+ "controller" => {
+ "config" => {
+ "enable-modsecurity" => "true",
+ "enable-owasp-modsecurity-crs" => "true"
+ }
+ }
+ }
+ end
+
+ def content_values
+ YAML.load_file(chart_values_file).deep_merge!(specification)
+ end
+
def application_jupyter_nil_or_installable?
cluster.application_jupyter.nil? || cluster.application_jupyter&.installable?
end
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index 7a61622b139..7855fb69bd6 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -37,18 +37,13 @@ module Clusters
has_one :platform_kubernetes, class_name: 'Clusters::Platforms::Kubernetes', inverse_of: :cluster, autosave: true
- 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 # rubocop:disable Rails/ReflectionClassName
- end
-
- has_one_cluster_application :helm
- has_one_cluster_application :ingress
- has_one_cluster_application :cert_manager
- has_one_cluster_application :prometheus
- has_one_cluster_application :runner
- has_one_cluster_application :jupyter
- has_one_cluster_application :knative
+ has_one :application_helm, class_name: 'Clusters::Applications::Helm'
+ has_one :application_ingress, class_name: 'Clusters::Applications::Ingress'
+ has_one :application_cert_manager, class_name: 'Clusters::Applications::CertManager'
+ has_one :application_prometheus, class_name: 'Clusters::Applications::Prometheus'
+ has_one :application_runner, class_name: 'Clusters::Applications::Runner'
+ has_one :application_jupyter, class_name: 'Clusters::Applications::Jupyter'
+ has_one :application_knative, class_name: 'Clusters::Applications::Knative'
has_many :kubernetes_namespaces
@@ -132,9 +127,15 @@ module Clusters
end
def applications
- APPLICATIONS.values.map do |application_class|
- public_send(application_class.association_name) || public_send("build_#{application_class.association_name}") # rubocop:disable GitlabSecurity/PublicSend
- end
+ [
+ application_helm || build_application_helm,
+ application_ingress || build_application_ingress,
+ application_cert_manager || build_application_cert_manager,
+ application_prometheus || build_application_prometheus,
+ application_runner || build_application_runner,
+ application_jupyter || build_application_jupyter,
+ application_knative || build_application_knative
+ ]
end
def provider
diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb
index d1b57a21a7d..803a65726d3 100644
--- a/app/models/clusters/concerns/application_core.rb
+++ b/app/models/clusters/concerns/application_core.rb
@@ -32,10 +32,6 @@ module Clusters
self.to_s.demodulize.underscore
end
- def self.association_name
- :"application_#{application_name}"
- end
-
def name
self.class.application_name
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index a442f607fbf..1470b50f396 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
# frozen_string_literal: true
class Commit
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index 57118bf7a6b..8b011bca72c 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -33,9 +33,16 @@ module Routable
#
# Returns a single object, or nil.
def find_by_full_path(path, follow_redirects: false)
- # Case sensitive match first (it's cheaper and the usual case)
- # If we didn't have an exact match, we perform a case insensitive search
- found = includes(:route).find_by(routes: { path: path }) || where_full_path_in([path]).take
+ routable_calls_counter.increment(method: 'find_by_full_path')
+
+ if Feature.enabled?(:routable_two_step_lookup)
+ # Case sensitive match first (it's cheaper and the usual case)
+ # If we didn't have an exact match, we perform a case insensitive search
+ found = includes(:route).find_by(routes: { path: path }) || where_full_path_in([path]).take
+ else
+ order_sql = Arel.sql("(CASE WHEN routes.path = #{connection.quote(path)} THEN 0 ELSE 1 END)")
+ found = where_full_path_in([path]).reorder(order_sql).take
+ end
return found if found
@@ -54,12 +61,19 @@ module Routable
def where_full_path_in(paths)
return none if paths.empty?
+ routable_calls_counter.increment(method: 'where_full_path_in')
+
wheres = paths.map do |path|
"(LOWER(routes.path) = LOWER(#{connection.quote(path)}))"
end
includes(:route).where(wheres.join(' OR ')).references(:routes)
end
+
+ # Temporary instrumentation of method calls
+ def routable_calls_counter
+ @routable_calls_counter ||= Gitlab::Metrics.counter(:gitlab_routable_calls_total, 'Number of calls to Routable by method')
+ end
end
def full_name
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index aa7286a9971..0b00cf10714 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -108,10 +108,13 @@ class DiffNote < Note
end
def fetch_diff_file
- return note_diff_file.raw_diff_file if note_diff_file
-
file =
- if created_at_diff?(noteable.diff_refs)
+ if note_diff_file
+ diff = Gitlab::Git::Diff.new(note_diff_file.to_hash)
+ Gitlab::Diff::File.new(diff,
+ repository: repository,
+ diff_refs: original_position.diff_refs)
+ elsif created_at_diff?(noteable.diff_refs)
# We're able to use the already persisted diffs (Postgres) if we're
# presenting a "current version" of the MR discussion diff.
# So no need to make an extra Gitaly diff request for it.
@@ -123,7 +126,9 @@ class DiffNote < Note
original_position.diff_file(repository)
end
- file&.unfold_diff_lines(position)
+ # Since persisted diff files already have its content "unfolded"
+ # there's no need to make it pass through the unfolding process.
+ file&.unfold_diff_lines(position) unless note_diff_file
file
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index ac26d29ad19..90061fe181e 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -454,17 +454,24 @@ class MergeRequest < ApplicationRecord
true
end
+ def preload_discussions_diff_highlight
+ preloadable_files = note_diff_files.for_commit_or_unresolved
+
+ discussions_diffs.load_highlight(preloadable_files.pluck(:id))
+ end
+
def discussions_diffs
strong_memoize(:discussions_diffs) do
- note_diff_files = NoteDiffFile
- .joins(:diff_note)
- .merge(notes.or(commit_notes))
- .includes(diff_note: :project)
-
Gitlab::DiscussionsDiff::FileCollection.new(note_diff_files.to_a)
end
end
+ def note_diff_files
+ NoteDiffFile
+ .where(diff_note: discussion_notes)
+ .includes(diff_note: :project)
+ end
+
def diff_size
# Calling `merge_request_diff.diffs.real_size` will also perform
# highlighting, which we don't need here.
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 800c492e8e2..4b9fee2bbdf 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -27,8 +27,11 @@ class Milestone < ApplicationRecord
belongs_to :project
belongs_to :group
- has_many :milestone_releases
- has_many :releases, through: :milestone_releases
+ # A one-to-one relationship is set up here as part of a MVC: https://gitlab.com/gitlab-org/gitlab-ce/issues/62402
+ # However, on the long term, we will want a many-to-many relationship between Release and Milestone.
+ # The "has_one through" allows us today to set up this one-to-one relationship while setting up the architecture for the long-term (ie intermediate table).
+ has_one :milestone_release
+ has_one :release, through: :milestone_release
has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.milestones&.maximum(:iid) }
has_internal_id :iid, scope: :group, init: ->(s) { s&.group&.milestones&.maximum(:iid) }
@@ -65,7 +68,7 @@ class Milestone < ApplicationRecord
validate :milestone_type_check
validate :start_date_should_be_less_than_due_date, if: proc { |m| m.start_date.present? && m.due_date.present? }
validate :dates_within_4_digits
- validates_associated :milestone_releases, message: -> (_, obj) { obj[:value].map(&:errors).map(&:full_messages).join(",") }
+ validates_associated :milestone_release, message: -> (_, obj) { obj[:value].errors.full_messages.join(",") }
strip_attributes :title
diff --git a/app/models/milestone_release.rb b/app/models/milestone_release.rb
index f7127df339d..c8743a8cad8 100644
--- a/app/models/milestone_release.rb
+++ b/app/models/milestone_release.rb
@@ -4,11 +4,9 @@ class MilestoneRelease < ApplicationRecord
belongs_to :milestone
belongs_to :release
+ validates :milestone_id, uniqueness: { scope: [:release_id] }
validate :same_project_between_milestone_and_release
- # Keep until 2019-11-29
- self.ignored_columns += %i[id]
-
private
def same_project_between_milestone_and_release
diff --git a/app/models/note_diff_file.rb b/app/models/note_diff_file.rb
index 67a6d5d6d6b..fcc9e2b3fd8 100644
--- a/app/models/note_diff_file.rb
+++ b/app/models/note_diff_file.rb
@@ -3,11 +3,15 @@
class NoteDiffFile < ApplicationRecord
include DiffFile
+ scope :for_commit_or_unresolved, -> do
+ joins(:diff_note).where("resolved_at IS NULL OR noteable_type = 'Commit'")
+ end
+
scope :referencing_sha, -> (oids, project_id:) do
joins(:diff_note).where(notes: { project_id: project_id, commit_id: oids })
end
- delegate :original_position, :project, :resolved_at, to: :diff_note
+ delegate :original_position, :project, to: :diff_note
belongs_to :diff_note, inverse_of: :note_diff_file
diff --git a/app/models/project.rb b/app/models/project.rb
index 57f1ca98ee2..96c715095f6 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -58,7 +58,6 @@ class Project < ApplicationRecord
ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT = 10
SORTING_PREFERENCE_FIELD = :projects_sort
- MAX_BUILD_TIMEOUT = 1.month
cache_markdown_field :description, pipeline: :description
@@ -431,7 +430,7 @@ class Project < ApplicationRecord
validates :build_timeout, allow_nil: true,
numericality: { greater_than_or_equal_to: 10.minutes,
- less_than: MAX_BUILD_TIMEOUT,
+ less_than: 1.month,
only_integer: true,
message: _('needs to be between 10 minutes and 1 month') }
@@ -753,15 +752,6 @@ class Project < ApplicationRecord
latest_successful_build_for_ref(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}"))
end
- def latest_pipeline_for_ref(ref = default_branch)
- ref = ref.presence || default_branch
- sha = commit(ref)&.sha
-
- return unless sha
-
- ci_pipelines.newest_first(ref: ref, sha: sha).first
- end
-
def merge_base_commit(first_commit_id, second_commit_id)
sha = repository.merge_base(first_commit_id, second_commit_id)
commit_by(oid: sha) if sha
diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb
index 0a498fde95a..8b79b5e9f0c 100644
--- a/app/models/project_services/bugzilla_service.rb
+++ b/app/models/project_services/bugzilla_service.rb
@@ -3,6 +3,8 @@
class BugzillaService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
+ prop_accessor :project_url, :issues_url, :new_issue_url
+
def default_title
'Bugzilla'
end
diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb
index dbc42b1b86d..535fcf6b94e 100644
--- a/app/models/project_services/custom_issue_tracker_service.rb
+++ b/app/models/project_services/custom_issue_tracker_service.rb
@@ -3,6 +3,8 @@
class CustomIssueTrackerService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
+ prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
+
def default_title
'Custom Issue Tracker'
end
diff --git a/app/models/project_services/data_fields.rb b/app/models/project_services/data_fields.rb
index 0f5385f8ce2..438d85098c8 100644
--- a/app/models/project_services/data_fields.rb
+++ b/app/models/project_services/data_fields.rb
@@ -3,56 +3,8 @@
module DataFields
extend ActiveSupport::Concern
- class_methods do
- # Provide convenient accessor methods for data fields.
- # TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
- def data_field(*args)
- args.each do |arg|
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- unless method_defined?(arg)
- def #{arg}
- data_fields.send('#{arg}') || (properties && properties['#{arg}'])
- end
- end
-
- def #{arg}=(value)
- @old_data_fields ||= {}
- @old_data_fields['#{arg}'] ||= #{arg} # set only on the first assignment, IOW we remember the original value only
- data_fields.send('#{arg}=', value)
- end
-
- def #{arg}_touched?
- @old_data_fields ||= {}
- @old_data_fields.has_key?('#{arg}')
- end
-
- def #{arg}_changed?
- #{arg}_touched? && @old_data_fields['#{arg}'] != #{arg}
- end
-
- def #{arg}_was
- return unless #{arg}_touched?
- return if data_fields.persisted? # arg_was does not work for attr_encrypted
-
- legacy_properties_data['#{arg}']
- end
- RUBY
- end
- end
- end
-
included do
- has_one :issue_tracker_data, autosave: true
- has_one :jira_tracker_data, autosave: true
-
- def data_fields
- raise NotImplementedError
- end
-
- def data_fields_present?
- data_fields.persisted?
- rescue NotImplementedError
- false
- end
+ has_one :issue_tracker_data
+ has_one :jira_tracker_data
end
end
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index ec28602b5e6..51032932eab 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -5,6 +5,8 @@ class GitlabIssueTrackerService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
+ prop_accessor :project_url, :issues_url, :new_issue_url
+
default_value_for :default, true
def default_title
diff --git a/app/models/project_services/issue_tracker_data.rb b/app/models/project_services/issue_tracker_data.rb
index b1d67657fe6..2c1d28ed421 100644
--- a/app/models/project_services/issue_tracker_data.rb
+++ b/app/models/project_services/issue_tracker_data.rb
@@ -6,6 +6,9 @@ class IssueTrackerData < ApplicationRecord
delegate :activated?, to: :service, allow_nil: true
validates :service, presence: true
+ validates :project_url, presence: true, public_url: { enforce_sanitization: true }, if: :activated?
+ validates :issues_url, presence: true, public_url: { enforce_sanitization: true }, if: :activated?
+ validates :new_issue_url, public_url: { enforce_sanitization: true }, if: :activated?
def self.encryption_options
{
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index c201bd2ea18..b6ad46513db 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -3,14 +3,9 @@
class IssueTrackerService < Service
validate :one_issue_tracker, if: :activated?, on: :manual_change
- # TODO: we can probably just delegate as part of
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
- data_field :project_url, :issues_url, :new_issue_url
-
default_value_for :category, 'issue_tracker'
- before_validation :handle_properties
- before_validation :set_default_data, on: :create
+ before_save :handle_properties
# Pattern used to extract links from comments
# Override this method on services that uses different patterns
@@ -48,31 +43,12 @@ class IssueTrackerService < Service
end
def handle_properties
- # this has been moved from initialize_properties and should be improved
- # as part of https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
- return unless properties
-
- @legacy_properties_data = properties.dup
- data_values = properties.slice!('title', 'description')
- properties.each do |key, _|
+ properties.slice('title', 'description').each do |key, _|
current_value = self.properties.delete(key)
value = attribute_changed?(key) ? attribute_change(key).last : current_value
write_attribute(key, value)
end
-
- data_values.reject! { |key| data_fields.changed.include?(key) }
- data_fields.assign_attributes(data_values) if data_values.present?
-
- self.properties = {}
- end
-
- def legacy_properties_data
- @legacy_properties_data ||= {}
- end
-
- def data_fields
- issue_tracker_data || self.build_issue_tracker_data
end
def default?
@@ -80,7 +56,7 @@ class IssueTrackerService < Service
end
def issue_url(iid)
- issues_url.gsub(':id', iid.to_s)
+ self.issues_url.gsub(':id', iid.to_s)
end
def issue_tracker_path
@@ -104,22 +80,25 @@ class IssueTrackerService < Service
]
end
- def initialize_properties
- {}
- end
-
# Initialize with default properties values
- def set_default_data
- return unless issues_tracker.present?
-
- self.title ||= issues_tracker['title']
-
- # we don't want to override if we have set something
- return if project_url || issues_url || new_issue_url
-
- data_fields.project_url = issues_tracker['project_url']
- data_fields.issues_url = issues_tracker['issues_url']
- data_fields.new_issue_url = issues_tracker['new_issue_url']
+ # or receive a block with custom properties
+ def initialize_properties(&block)
+ return unless properties.nil?
+
+ if enabled_in_gitlab_config
+ if block_given?
+ yield
+ else
+ self.properties = {
+ title: issues_tracker['title'],
+ project_url: issues_tracker['project_url'],
+ issues_url: issues_tracker['issues_url'],
+ new_issue_url: issues_tracker['new_issue_url']
+ }
+ end
+ else
+ self.properties = {}
+ end
end
def self.supported_events
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 61ae78a0b95..0728c83005e 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -17,10 +17,7 @@ class JiraService < IssueTrackerService
# Jira Cloud version is deprecating authentication via username and password.
# We should use username/password for Jira Server and email/api_token for Jira Cloud,
# for more information check: https://gitlab.com/gitlab-org/gitlab-ce/issues/49936.
-
- # TODO: we can probably just delegate as part of
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/63084
- data_field :username, :password, :url, :api_url, :jira_issue_transition_id
+ prop_accessor :username, :password, :url, :api_url, :jira_issue_transition_id
before_update :reset_password
@@ -38,34 +35,24 @@ class JiraService < IssueTrackerService
end
def initialize_properties
- {}
- end
-
- def data_fields
- jira_tracker_data || self.build_jira_tracker_data
+ super do
+ self.properties = {
+ url: issues_tracker['url'],
+ api_url: issues_tracker['api_url']
+ }
+ end
end
def reset_password
- data_fields.password = nil if reset_password?
- end
-
- def set_default_data
- return unless issues_tracker.present?
-
- self.title ||= issues_tracker['title']
-
- return if url
-
- data_fields.url ||= issues_tracker['url']
- data_fields.api_url ||= issues_tracker['api_url']
+ self.password = nil if reset_password?
end
def options
url = URI.parse(client_url)
{
- username: username,
- password: password,
+ username: self.username,
+ password: self.password,
site: URI.join(url, '/').to_s, # Intended to find the root
context_path: url.path,
auth_type: :basic,
diff --git a/app/models/project_services/jira_tracker_data.rb b/app/models/project_services/jira_tracker_data.rb
index e4e0f64150a..4f528e3d81b 100644
--- a/app/models/project_services/jira_tracker_data.rb
+++ b/app/models/project_services/jira_tracker_data.rb
@@ -6,6 +6,13 @@ class JiraTrackerData < ApplicationRecord
delegate :activated?, to: :service, allow_nil: true
validates :service, presence: true
+ validates :url, public_url: { enforce_sanitization: true }, presence: true, if: :activated?
+ validates :api_url, public_url: { enforce_sanitization: true }, allow_blank: true
+ validates :username, presence: true, if: :activated?
+ validates :password, presence: true, if: :activated?
+ validates :jira_issue_transition_id,
+ format: { with: Gitlab::Regex.jira_transition_id_regex, message: s_("JiraService|transition ids can have only numbers which can be split with , or ;") },
+ allow_blank: true
def self.encryption_options
{
diff --git a/app/models/project_services/redmine_service.rb b/app/models/project_services/redmine_service.rb
index a4ca0d20669..5ca057ca833 100644
--- a/app/models/project_services/redmine_service.rb
+++ b/app/models/project_services/redmine_service.rb
@@ -3,6 +3,8 @@
class RedmineService < IssueTrackerService
validates :project_url, :issues_url, :new_issue_url, presence: true, public_url: true, if: :activated?
+ prop_accessor :project_url, :issues_url, :new_issue_url
+
def default_title
'Redmine'
end
diff --git a/app/models/project_services/youtrack_service.rb b/app/models/project_services/youtrack_service.rb
index 0416eaa5be0..f9de1f7dc49 100644
--- a/app/models/project_services/youtrack_service.rb
+++ b/app/models/project_services/youtrack_service.rb
@@ -3,6 +3,8 @@
class YoutrackService < IssueTrackerService
validates :project_url, :issues_url, presence: true, public_url: true, if: :activated?
+ prop_accessor :project_url, :issues_url
+
# {PROJECT-KEY}-{NUMBER} Examples: YT-1, PRJ-1, gl-030
def self.reference_pattern(only_long: false)
if only_long
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 1857a59e01c..8769d3eb916 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -40,11 +40,6 @@ class ProtectedBranch < ApplicationRecord
def self.protected_refs(project)
project.protected_branches.select(:name)
end
-
- def self.branch_requires_code_owner_approval?(project, branch_name)
- # NOOP
- #
- end
end
ProtectedBranch.prepend_if_ee('EE::ProtectedBranch')
diff --git a/app/models/release.rb b/app/models/release.rb
index cd63b4d5fef..b2e65974aa0 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -12,8 +12,11 @@ class Release < ApplicationRecord
has_many :links, class_name: 'Releases::Link'
- has_many :milestone_releases
- has_many :milestones, through: :milestone_releases
+ # A one-to-one relationship is set up here as part of a MVC: https://gitlab.com/gitlab-org/gitlab-ce/issues/62402
+ # However, on the long term, we will want a many-to-many relationship between Release and Milestone.
+ # The "has_one through" allows us today to set up this one-to-one relationship while setting up the architecture for the long-term (ie intermediate table).
+ has_one :milestone_release
+ has_one :milestone, through: :milestone_release
default_value_for :released_at, allows_nil: false do
Time.zone.now
@@ -23,7 +26,7 @@ class Release < ApplicationRecord
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(",") }
+ validates_associated :milestone_release, message: -> (_, obj) { obj[:value].errors.full_messages.join(",") }
scope :sorted, -> { order(released_at: :desc) }
diff --git a/app/presenters/ci/build_metadata_presenter.rb b/app/presenters/ci/build_metadata_presenter.rb
index 4871bb3a919..015b1f67db7 100644
--- a/app/presenters/ci/build_metadata_presenter.rb
+++ b/app/presenters/ci/build_metadata_presenter.rb
@@ -5,8 +5,7 @@ module Ci
TIMEOUT_SOURCES = {
unknown_timeout_source: nil,
project_timeout_source: 'project',
- runner_timeout_source: 'runner',
- job_timeout_source: 'job'
+ runner_timeout_source: 'runner'
}.freeze
presents :metadata
diff --git a/app/services/clusters/applications/base_service.rb b/app/services/clusters/applications/base_service.rb
index 67fb3ac8355..a9feb60be6e 100644
--- a/app/services/clusters/applications/base_service.rb
+++ b/app/services/clusters/applications/base_service.rb
@@ -77,10 +77,6 @@ module Clusters
params[:application]
end
- def application_class
- Clusters::Cluster::APPLICATIONS[application_name]
- end
-
def create_oauth_application(application, request)
oauth_application_params = {
name: params[:application],
diff --git a/app/services/clusters/applications/create_service.rb b/app/services/clusters/applications/create_service.rb
index 2a626a402e4..f723c42c049 100644
--- a/app/services/clusters/applications/create_service.rb
+++ b/app/services/clusters/applications/create_service.rb
@@ -10,7 +10,7 @@ module Clusters
end
def builder
- cluster.public_send(application_class.association_name) || # rubocop:disable GitlabSecurity/PublicSend
+ cluster.public_send(:"application_#{application_name}") || # rubocop:disable GitlabSecurity/PublicSend
cluster.public_send(:"build_application_#{application_name}") # rubocop:disable GitlabSecurity/PublicSend
end
end
diff --git a/app/services/clusters/applications/destroy_service.rb b/app/services/clusters/applications/destroy_service.rb
index d666682487b..f3a4c4f754a 100644
--- a/app/services/clusters/applications/destroy_service.rb
+++ b/app/services/clusters/applications/destroy_service.rb
@@ -16,7 +16,7 @@ module Clusters
private
def builder
- cluster.public_send(application_class.association_name) # rubocop:disable GitlabSecurity/PublicSend
+ cluster.public_send(:"application_#{application_name}") # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/app/services/clusters/applications/update_service.rb b/app/services/clusters/applications/update_service.rb
index 7a36401f156..0fa937da865 100644
--- a/app/services/clusters/applications/update_service.rb
+++ b/app/services/clusters/applications/update_service.rb
@@ -10,7 +10,7 @@ module Clusters
end
def builder
- cluster.public_send(application_class.association_name) # rubocop:disable GitlabSecurity/PublicSend
+ cluster.public_send(:"application_#{application_name}") # rubocop:disable GitlabSecurity/PublicSend
end
end
end
diff --git a/app/services/projects/create_from_template_service.rb b/app/services/projects/create_from_template_service.rb
index 91ece024e13..31977a7c76a 100644
--- a/app/services/projects/create_from_template_service.rb
+++ b/app/services/projects/create_from_template_service.rb
@@ -9,9 +9,7 @@ module Projects
end
def execute
- return project unless validate_template!
-
- file = built_in_template&.file
+ file = Gitlab::ProjectTemplate.find(template_name)&.file
override_params = params.dup
params[:file] = file
@@ -26,25 +24,6 @@ module Projects
params.delete(:template_name).presence
end
end
-
- private
-
- def validate_template!
- return true if built_in_template
-
- project.errors.add(:template_name, _("'%{template_name}' is unknown or invalid" % { template_name: template_name }))
- false
- end
-
- def built_in_template
- strong_memoize(:built_in_template) do
- Gitlab::ProjectTemplate.find(template_name)
- end
- end
-
- def project
- @project ||= ::Project.new(namespace_id: params[:namespace_id])
- end
end
end
diff --git a/app/services/releases/concerns.rb b/app/services/releases/concerns.rb
index a0ebaea77c8..b5412e97284 100644
--- a/app/services/releases/concerns.rb
+++ b/app/services/releases/concerns.rb
@@ -48,29 +48,25 @@ module Releases
end
end
- def milestones
- return [] unless param_for_milestone_titles_provided?
+ def milestone
+ return unless params[:milestone]
- strong_memoize(:milestones) do
+ strong_memoize(:milestone) do
MilestonesFinder.new(
project: project,
current_user: current_user,
project_ids: Array(project.id),
- state: 'all',
- title: params[:milestones]
- ).execute
+ title: params[:milestone]
+ ).execute.first
end
end
- def inexistent_milestones
- return [] unless param_for_milestone_titles_provided?
-
- existing_milestone_titles = milestones.map(&:title)
- Array(params[:milestones]) - existing_milestone_titles
+ def inexistent_milestone?
+ params[:milestone] && !params[:milestone].empty? && !milestone
end
- def param_for_milestone_titles_provided?
- params.key?(:milestones)
+ def param_for_milestone_title_provided?
+ params[:milestone].present? || params[:milestone]&.empty?
end
end
end
diff --git a/app/services/releases/create_service.rb b/app/services/releases/create_service.rb
index 9a0a876454f..c91d43084d3 100644
--- a/app/services/releases/create_service.rb
+++ b/app/services/releases/create_service.rb
@@ -7,7 +7,7 @@ module Releases
def execute
return error('Access Denied', 403) unless allowed?
return error('Release already exists', 409) if release
- return error("Milestone(s) not found: #{inexistent_milestones.join(', ')}", 400) if inexistent_milestones.any?
+ return error('Milestone does not exist', 400) if inexistent_milestone?
tag = ensure_tag
@@ -61,7 +61,7 @@ module Releases
sha: tag.dereferenced_target.sha,
released_at: released_at,
links_attributes: params.dig(:assets, 'links') || [],
- milestones: milestones
+ milestone: milestone
)
end
end
diff --git a/app/services/releases/update_service.rb b/app/services/releases/update_service.rb
index 7aa51c4a332..70acc68f747 100644
--- a/app/services/releases/update_service.rb
+++ b/app/services/releases/update_service.rb
@@ -9,9 +9,9 @@ module Releases
return error('Release does not exist', 404) unless release
return error('Access Denied', 403) unless allowed?
return error('params is empty', 400) if empty_params?
- return error("Milestone(s) not found: #{inexistent_milestones.join(', ')}", 400) if inexistent_milestones.any?
+ return error('Milestone does not exist', 400) if inexistent_milestone?
- params[:milestones] = milestones if param_for_milestone_titles_provided?
+ params[:milestone] = milestone if param_for_milestone_title_provided?
if release.update(params)
success(tag: existing_tag, release: release)
diff --git a/app/services/service_response.rb b/app/services/service_response.rb
index 08b7e9d0831..f3437ba16de 100644
--- a/app/services/service_response.rb
+++ b/app/services/service_response.rb
@@ -1,8 +1,8 @@
# frozen_string_literal: true
class ServiceResponse
- def self.success(message: nil, payload: {}, http_status: :ok)
- new(status: :success, message: message, payload: payload, http_status: http_status)
+ def self.success(message: nil, payload: {})
+ new(status: :success, message: message, payload: payload)
end
def self.error(message:, payload: {}, http_status: nil)
diff --git a/app/views/admin/application_settings/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml
index 4358365504a..9ed4bc44aae 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-account-settings'), html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-account-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_diff_limits.html.haml b/app/views/admin/application_settings/_diff_limits.html.haml
index 137b7281e0f..408e569fe07 100644
--- a/app/views/admin/application_settings/_diff_limits.html.haml
+++ b/app/views/admin/application_settings/_diff_limits.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-merge-request-settings'), html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-merge-request-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_external_authorization_service_form.html.haml b/app/views/admin/application_settings/_external_authorization_service_form.html.haml
index 73412133979..7587ecbf9d3 100644
--- a/app/views/admin/application_settings/_external_authorization_service_form.html.haml
+++ b/app/views/admin/application_settings/_external_authorization_service_form.html.haml
@@ -8,7 +8,7 @@
= _('External Classification Policy Authorization')
.settings-content
- = form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-external-auth-settings'), html: { class: 'fieldset-form' } do |f|
+ = form_for @application_setting, url: admin_application_settings_path(anchor: 'js-external-auth-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_signin.html.haml b/app/views/admin/application_settings/_signin.html.haml
index 0e45301b598..5f36358f599 100644
--- a/app/views/admin/application_settings/_signin.html.haml
+++ b/app/views/admin/application_settings/_signin.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-signin-settings'), html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-signin-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_signup.html.haml b/app/views/admin/application_settings/_signup.html.haml
index 7c1df78f30c..a0a58b811ee 100644
--- a/app/views/admin/application_settings/_signup.html.haml
+++ b/app/views/admin/application_settings/_signup.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-signup-settings'), html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-signup-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_terminal.html.haml b/app/views/admin/application_settings/_terminal.html.haml
index 654aed54a15..49980e1e1a7 100644
--- a/app/views/admin/application_settings/_terminal.html.haml
+++ b/app/views/admin/application_settings/_terminal.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-terminal-settings'), html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-terminal-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_terms.html.haml b/app/views/admin/application_settings/_terms.html.haml
index 19e7ab7c99a..ef58e9b1128 100644
--- a/app/views/admin/application_settings/_terms.html.haml
+++ b/app/views/admin/application_settings/_terms.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-terms-settings'), html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-terms-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/_visibility_and_access.html.haml b/app/views/admin/application_settings/_visibility_and_access.html.haml
index e57ef1ea18f..c07bafbe302 100644
--- a/app/views/admin/application_settings/_visibility_and_access.html.haml
+++ b/app/views/admin/application_settings/_visibility_and_access.html.haml
@@ -1,4 +1,4 @@
-= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-visibility-settings'), html: { class: 'fieldset-form' } do |f|
+= form_for @application_setting, url: admin_application_settings_path(anchor: 'js-visibility-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/show.html.haml
index b9f49fdc9de..31f18ba0d56 100644
--- a/app/views/admin/application_settings/general.html.haml
+++ b/app/views/admin/application_settings/show.html.haml
@@ -1,5 +1,5 @@
-- breadcrumb_title _("General")
-- page_title _("General")
+- breadcrumb_title _("Settings")
+- page_title _("Settings")
- @content_class = "limit-container-width" unless fluid_layout
%section.settings.as-visibility-access.no-animate#js-visibility-settings{ class: ('expanded' if expanded_by_default?) }
@@ -90,7 +90,7 @@
%p
= _('Manage Web IDE features')
.settings-content
- = form_for @application_setting, url: general_admin_application_settings_path(anchor: "#js-web-ide-settings"), html: { class: 'fieldset-form' } do |f|
+ = form_for @application_setting, url: admin_application_settings_path(anchor: "#js-web-ide-settings"), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 784fe556123..f76268bc29b 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -232,7 +232,7 @@
= _('Settings')
%li.divider.fly-out-top-item
= nav_link(path: 'application_settings#show') do
- = link_to general_admin_application_settings_path, title: _('General'), class: 'qa-admin-settings-general-item' do
+ = link_to admin_application_settings_path, title: _('General'), class: 'qa-admin-settings-general-item' do
%span
= _('General')
= nav_link(path: 'application_settings#integrations') do
diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
index 104c68919f0..84f0900d9c1 100644
--- a/app/views/projects/mirrors/_mirror_repos.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -49,12 +49,12 @@
= render_if_exists 'projects/mirrors/table_pull_row'
- @project.remote_mirrors.each_with_index do |mirror, index|
- next if mirror.new_record?
- %tr.rspec-mirrored-repository-row{ class: ('bg-secondary' if mirror.disabled?), data: { qa_selector: 'mirrored_repository_row' } }
- %td{ data: { qa_selector: 'mirror_repository_url_cell' } }= mirror.safe_url || _('Invalid URL')
+ %tr.qa-mirrored-repository-row.rspec-mirrored-repository-row{ class: ('bg-secondary' if mirror.disabled?) }
+ %td.qa-mirror-repository-url= mirror.safe_url || _('Invalid URL')
%td= _('Push')
%td
= mirror.last_update_started_at.present? ? time_ago_with_tooltip(mirror.last_update_started_at) : _('Never')
- %td{ data: { qa_selector: 'mirror_last_update_at_cell' } }= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
+ %td.qa-mirror-last-update-at= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
%td
- if mirror.disabled?
= render 'projects/mirrors/disabled_mirror_badge'
diff --git a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
index bba4949277d..3644a623d2c 100644
--- a/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/shared/_create_protected_branch.html.haml
@@ -29,4 +29,4 @@
= yield :push_access_levels
.card-footer
- = f.submit 'Protect', class: 'btn-success btn', disabled: true, data: { qa_selector: 'protect_button' }
+ = f.submit 'Protect', class: 'btn-success btn', disabled: true
diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml
index d0d06a0df7e..e34973f1f43 100644
--- a/app/views/projects/registry/repositories/index.html.haml
+++ b/app/views/projects/registry/repositories/index.html.haml
@@ -2,7 +2,7 @@
.row.registry-placeholder.prepend-bottom-10
.col-12
#js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json),
- "help_page_path" => help_page_path('user/packages/container_registry/index'),
+ "help_page_path" => help_page_path('user/project/container_registry'),
"no_containers_image" => image_path('illustrations/docker-empty-state.svg'),
"containers_error_image" => image_path('illustrations/docker-error-state.svg'),
"repository_url" => escape_once(@project.container_registry_url),
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 959a2423e02..2f277e8147a 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -2,10 +2,8 @@
.col-lg-3
%h4.prepend-top-0
= @service.title
- - [true, false].each do |value|
- - hide_class = 'd-none' if @service.activated? != value
- %span.js-service-active-status{ class: hide_class, data: { value: value.to_s } }
- = boolean_to_icon value
+ = boolean_to_icon @service.activated?
+
%p= #{@service.description}.
- if @service.respond_to?(:detailed_description)
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index bcce7cb52fb..573ed36d7f4 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -60,11 +60,6 @@
.controls.d-flex.flex-sm-column.align-items-center.align-items-sm-end.flex-wrap.flex-shrink-0.text-secondary{ class: css_controls_class }
.icon-container.d-flex.align-items-center
- - if pipeline_status && can?(current_user, :read_cross_project) && project.pipeline_status.has_status? && can?(current_user, :read_build, project)
- - pipeline_path = pipelines_project_commit_path(project.pipeline_status.project, project.pipeline_status.sha, ref: project.pipeline_status.ref)
- %span.icon-wrapper.pipeline-status
- = render 'ci/status/icon', status: project.commit.last_pipeline.detailed_status(current_user), tooltip_placement: 'top', path: pipeline_path
-
- if project.archived
%span.d-flex.icon-wrapper.badge.badge-warning archived
- if stars
@@ -91,6 +86,10 @@
title: _('Issues'), data: { container: 'body', placement: 'top' } do
= sprite_icon('issues', size: 14, css_class: 'append-right-4')
= number_with_delimiter(project.open_issues_count)
+ - if pipeline_status && can?(current_user, :read_cross_project) && project.pipeline_status.has_status? && can?(current_user, :read_build, project)
+ - pipeline_path = pipelines_project_commit_path(project.pipeline_status.project, project.pipeline_status.sha, ref: project.pipeline_status.ref)
+ %span.icon-wrapper.pipeline-status
+ = render 'ci/status/icon', status: project.commit.last_pipeline.detailed_status(current_user), tooltip_placement: 'top', path: pipeline_path
.updated-note
%span
= _('Updated')
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index 73401029da4..2d2382e469a 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -22,7 +22,7 @@
= f.label :file_name, "File"
.col-sm-10
.file-holder.snippet
- .js-file-title.file-title-flex-parent
+ .js-file-title.file-title
= f.text_field :file_name, placeholder: "Optionally name this file to add code highlighting, e.g. example.rb for Ruby.", class: 'form-control snippet-file-name qa-snippet-file-name'
.file-content.code
%pre#editor= @snippet.content
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index 69481293f90..1a9ae68f53d 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -28,7 +28,7 @@
= @snippet.description
- if @snippet.updated_at != @snippet.created_at
- = edited_time_ago_with_tooltip(@snippet, placement: 'bottom', exclude_author: true)
+ = edited_time_ago_with_tooltip(@snippet, placement: 'bottom', html_class: 'snippet-edited-ago', exclude_author: true)
- if @snippet.embeddable?
.embed-snippet