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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-12 15:10:25 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-12 15:10:25 +0300
commit999f47e9e6da399de9dc1de404f073f7dd30af0d (patch)
tree926bf806abb6705632dc19ceb75269f40a4c9fac /app
parent20bd3b7d4ebb1d7ebef305656b156313d09a6674 (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/lib/utils/axios_startup_calls.js19
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue44
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard_header.vue4
-rw-r--r--app/assets/javascripts/monitoring/queries/getAnnotations.query.graphql1
-rw-r--r--app/assets/javascripts/monitoring/queries/getEnvironments.query.graphql1
-rw-r--r--app/assets/javascripts/monitoring/router/routes.js4
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue4
-rw-r--r--app/assets/javascripts/notes/stores/actions.js49
-rw-r--r--app/assets/javascripts/pages/projects/commit/show/index.js50
-rw-r--r--app/assets/javascripts/repository/components/tree_content.vue36
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue35
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/edit_form.vue32
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue20
-rw-r--r--app/assets/stylesheets/pages/editor.scss3
-rw-r--r--app/controllers/groups/releases_controller.rb23
-rw-r--r--app/controllers/projects/commit_controller.rb8
-rw-r--r--app/controllers/projects/environments_controller.rb1
-rw-r--r--app/controllers/projects/metrics_dashboard_controller.rb1
-rw-r--r--app/finders/releases_finder.rb41
-rw-r--r--app/helpers/search_helper.rb1
-rw-r--r--app/models/ci/pipeline_artifact.rb2
-rw-r--r--app/models/concerns/issuable.rb2
-rw-r--r--app/serializers/release_entity.rb6
-rw-r--r--app/serializers/release_serializer.rb5
-rw-r--r--app/services/metrics/dashboard/base_service.rb1
-rw-r--r--app/services/metrics/dashboard/clone_dashboard_service.rb6
-rw-r--r--app/services/metrics/dashboard/cluster_dashboard_service.rb3
-rw-r--r--app/services/metrics/dashboard/custom_metric_embed_service.rb1
-rw-r--r--app/services/metrics/dashboard/pod_dashboard_service.rb3
-rw-r--r--app/services/metrics/dashboard/predefined_dashboard_service.rb3
-rw-r--r--app/services/metrics/dashboard/self_monitoring_dashboard_service.rb3
-rw-r--r--app/services/metrics/dashboard/system_dashboard_service.rb1
-rw-r--r--app/views/projects/commit/diff_files.html.haml3
-rw-r--r--app/views/projects/diffs/_diffs.html.haml10
-rw-r--r--app/views/shared/members/_member.html.haml11
35 files changed, 332 insertions, 105 deletions
diff --git a/app/assets/javascripts/lib/utils/axios_startup_calls.js b/app/assets/javascripts/lib/utils/axios_startup_calls.js
index cb2e8a76c08..a047cebc8ab 100644
--- a/app/assets/javascripts/lib/utils/axios_startup_calls.js
+++ b/app/assets/javascripts/lib/utils/axios_startup_calls.js
@@ -34,14 +34,17 @@ const setupAxiosStartupCalls = axios => {
});
// eslint-disable-next-line promise/no-nesting
- return res.json().then(data => ({
- data,
- status: res.status,
- statusText: res.statusText,
- headers: fetchHeaders,
- config: req,
- request: req,
- }));
+ return res
+ .clone()
+ .json()
+ .then(data => ({
+ data,
+ status: res.status,
+ statusText: res.statusText,
+ headers: fetchHeaders,
+ config: req,
+ request: req,
+ }));
});
}
diff --git a/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue b/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
index 54586c67fef..d82b8e0992e 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_actions_menu.vue
@@ -11,6 +11,8 @@ import {
GlTooltipDirective,
} from '@gitlab/ui';
import CustomMetricsFormFields from '~/custom_metrics/components/custom_metrics_form_fields.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { PANEL_NEW_PAGE } from '../router/constants';
import DuplicateDashboardModal from './duplicate_dashboard_modal.vue';
import CreateDashboardModal from './create_dashboard_modal.vue';
import { s__ } from '~/locale';
@@ -36,6 +38,7 @@ export default {
GlTooltip: GlTooltipDirective,
TrackEvent: TrackEventDirective,
},
+ mixins: [glFeatureFlagsMixin()],
props: {
addingMetricsAvailable: {
type: Boolean,
@@ -56,6 +59,10 @@ export default {
type: String,
required: true,
},
+ isOotbDashboard: {
+ type: Boolean,
+ required: true,
+ },
},
data() {
return { customMetricsFormIsValid: null };
@@ -72,15 +79,22 @@ export default {
},
isMenuItemEnabled() {
return {
+ addPanel: !this.isOotbDashboard,
createDashboard: Boolean(this.projectPath),
editDashboard: this.selectedDashboard?.can_edit,
};
},
isMenuItemShown() {
return {
+ addPanel: this.glFeatures.metricsDashboardNewPanelPage,
duplicateDashboard: this.isOutOfTheBoxDashboard,
};
},
+ newPanelPageLocation() {
+ // Retains params/query if any
+ const { params, query } = this.$route ?? {};
+ return { name: PANEL_NEW_PAGE, params, query };
+ },
},
methods: {
...mapActions('monitoringDashboard', ['toggleStarredValue']),
@@ -117,7 +131,9 @@ export default {
starDashboard: s__('Metrics|Star dashboard'),
unstarDashboard: s__('Metrics|Unstar dashboard'),
addMetric: s__('Metrics|Add metric'),
- editDashboardInfo: s__('Metrics|Duplicate this dashboard to edit dashboard YAML'),
+ addPanel: s__('Metrics|Add panel'),
+ addPanelInfo: s__('Metrics|Duplicate this dashboard to add panel or edit dashboard YAML.'),
+ editDashboardInfo: s__('Metrics|Duplicate this dashboard to add panel or edit dashboard YAML.'),
editDashboard: s__('Metrics|Edit dashboard YAML'),
createDashboard: s__('Metrics|Create new dashboard'),
},
@@ -176,6 +192,32 @@ export default {
</gl-modal>
</template>
+ <template v-if="isMenuItemShown.addPanel">
+ <gl-new-dropdown-item
+ v-if="isMenuItemEnabled.addPanel"
+ data-testid="add-panel-item-enabled"
+ :to="newPanelPageLocation"
+ >
+ {{ $options.i18n.addPanel }}
+ </gl-new-dropdown-item>
+
+ <!--
+ wrapper for tooltip as button can be `disabled`
+ https://bootstrap-vue.org/docs/components/tooltip#disabled-elements
+ -->
+ <div v-else v-gl-tooltip :title="$options.i18n.addPanelInfo">
+ <gl-new-dropdown-item
+ :alt="$options.i18n.addPanelInfo"
+ :to="newPanelPageLocation"
+ data-testid="add-panel-item-disabled"
+ disabled
+ class="gl-cursor-not-allowed"
+ >
+ <span class="gl-text-gray-400">{{ $options.i18n.addPanel }}</span>
+ </gl-new-dropdown-item>
+ </div>
+ </template>
+
<gl-new-dropdown-item
v-if="isMenuItemEnabled.editDashboard"
:href="selectedDashboard ? selectedDashboard.project_blob_path : null"
diff --git a/app/assets/javascripts/monitoring/components/dashboard_header.vue b/app/assets/javascripts/monitoring/components/dashboard_header.vue
index 1c921548ce7..1048e72a431 100644
--- a/app/assets/javascripts/monitoring/components/dashboard_header.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard_header.vue
@@ -118,6 +118,9 @@ export default {
shouldShowSettingsButton() {
return this.canAccessOperationsSettings && this.operationsSettingsPath;
},
+ isOOTBDashboard() {
+ return this.selectedDashboard?.out_of_the_box_dashboard ?? false;
+ },
},
methods: {
...mapActions('monitoringDashboard', ['filterEnvironments']),
@@ -266,6 +269,7 @@ export default {
:custom-metrics-path="customMetricsPath"
:validate-query-path="validateQueryPath"
:default-branch="defaultBranch"
+ :is-ootb-dashboard="isOOTBDashboard"
/>
</div>
diff --git a/app/assets/javascripts/monitoring/queries/getAnnotations.query.graphql b/app/assets/javascripts/monitoring/queries/getAnnotations.query.graphql
index 27b49860b8a..32b982ff195 100644
--- a/app/assets/javascripts/monitoring/queries/getAnnotations.query.graphql
+++ b/app/assets/javascripts/monitoring/queries/getAnnotations.query.graphql
@@ -5,6 +5,7 @@ query getAnnotations(
$startingFrom: Time!
) {
project(fullPath: $projectPath) {
+ id
environments(name: $environmentName) {
nodes {
id
diff --git a/app/assets/javascripts/monitoring/queries/getEnvironments.query.graphql b/app/assets/javascripts/monitoring/queries/getEnvironments.query.graphql
index 17cd1b2c342..48d0a780fc7 100644
--- a/app/assets/javascripts/monitoring/queries/getEnvironments.query.graphql
+++ b/app/assets/javascripts/monitoring/queries/getEnvironments.query.graphql
@@ -1,5 +1,6 @@
query getEnvironments($projectPath: ID!, $search: String, $states: [String!]) {
project(fullPath: $projectPath) {
+ id
data: environments(search: $search, states: $states) {
environments: nodes {
name
diff --git a/app/assets/javascripts/monitoring/router/routes.js b/app/assets/javascripts/monitoring/router/routes.js
index 8092a5b7c0b..cc43fd8622a 100644
--- a/app/assets/javascripts/monitoring/router/routes.js
+++ b/app/assets/javascripts/monitoring/router/routes.js
@@ -13,12 +13,12 @@ import { DASHBOARD_PAGE, PANEL_NEW_PAGE } from './constants';
export default [
{
name: PANEL_NEW_PAGE,
- path: '/:dashboard(.*)?/panel/new',
+ path: '/:dashboard(.+)?/panel/new',
component: PanelNewPage,
},
{
name: DASHBOARD_PAGE,
- path: '/:dashboard(.*)?',
+ path: '/:dashboard(.+)?',
component: DashboardPage,
},
];
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index faa6006945d..262c0b53e79 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -136,6 +136,8 @@ export default {
}
window.addEventListener('hashchange', this.handleHashChanged);
+
+ eventHub.$on('notesApp.updateIssuableConfidentiality', this.setConfidentiality);
},
updated() {
this.$nextTick(() => {
@@ -146,6 +148,7 @@ export default {
beforeDestroy() {
this.stopPolling();
window.removeEventListener('hashchange', this.handleHashChanged);
+ eventHub.$off('notesApp.updateIssuableConfidentiality', this.setConfidentiality);
},
methods: {
...mapActions([
@@ -164,6 +167,7 @@ export default {
'startTaskList',
'convertToDiscussion',
'stopPolling',
+ 'setConfidentiality',
]),
discussionIsIndividualNoteAndNotConverted(discussion) {
return discussion.individual_note && !this.convertedDisscussionIds.includes(discussion.id);
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index ac57fcf2ea5..3f4da32ec35 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -21,29 +21,6 @@ import Api from '~/api';
let eTagPoll;
-export const updateConfidentialityOnIssue = ({ commit, getters }, { confidential, fullPath }) => {
- const { iid } = getters.getNoteableData;
-
- return utils.gqClient
- .mutate({
- mutation: updateIssueConfidentialMutation,
- variables: {
- input: {
- projectPath: fullPath,
- iid: String(iid),
- confidential,
- },
- },
- })
- .then(({ data }) => {
- const {
- issueSetConfidential: { issue },
- } = data;
-
- commit(types.SET_ISSUE_CONFIDENTIAL, issue.confidential);
- });
-};
-
export const updateLockedAttribute = ({ commit, getters }, { locked, fullPath }) => {
const { iid, targetType } = getters.getNoteableData;
@@ -712,3 +689,29 @@ export const updateAssignees = ({ commit }, assignees) => {
export const updateDiscussionPosition = ({ commit }, updatedPosition) => {
commit(types.UPDATE_DISCUSSION_POSITION, updatedPosition);
};
+
+export const updateConfidentialityOnIssuable = (
+ { getters, commit },
+ { confidential, fullPath },
+) => {
+ const { iid } = getters.getNoteableData;
+
+ return utils.gqClient
+ .mutate({
+ mutation: updateIssueConfidentialMutation,
+ variables: {
+ input: {
+ projectPath: fullPath,
+ iid: String(iid),
+ confidential,
+ },
+ },
+ })
+ .then(({ data }) => {
+ const {
+ issueSetConfidential: { issue },
+ } = data;
+
+ setConfidentiality({ commit }, issue.confidential);
+ });
+};
diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js
index 0eb6f231839..a245af72d93 100644
--- a/app/assets/javascripts/pages/projects/commit/show/index.js
+++ b/app/assets/javascripts/pages/projects/commit/show/index.js
@@ -7,23 +7,47 @@ import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
import initNotes from '~/init_notes';
import initChangesDropdown from '~/init_changes_dropdown';
-import initDiffNotes from '~/diff_notes/diff_notes_bundle';
import { fetchCommitMergeRequests } from '~/commit_merge_requests';
import '~/sourcegraph/load';
+import { handleLocationHash } from '~/lib/utils/common_utils';
+import axios from '~/lib/utils/axios_utils';
+import syntaxHighlight from '~/syntax_highlight';
+import flash from '~/flash';
+import { __ } from '~/locale';
document.addEventListener('DOMContentLoaded', () => {
const hasPerfBar = document.querySelector('.with-performance-bar');
const performanceHeight = hasPerfBar ? 35 : 0;
- new Diff();
- new ZenMode();
- new ShortcutsNavigation();
- new MiniPipelineGraph({
- container: '.js-commit-pipeline-graph',
- }).bindEvents();
- initNotes();
- initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight + performanceHeight);
- // eslint-disable-next-line no-jquery/no-load
- $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
- fetchCommitMergeRequests();
- initDiffNotes();
+ const filesContainer = $('.js-diffs-batch');
+ const initAfterPageLoad = () => {
+ new Diff();
+ new ZenMode();
+ new ShortcutsNavigation();
+ new MiniPipelineGraph({
+ container: '.js-commit-pipeline-graph',
+ }).bindEvents();
+ initNotes();
+ initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight + performanceHeight);
+ // eslint-disable-next-line no-jquery/no-load
+ $('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
+ fetchCommitMergeRequests();
+ };
+
+ if (filesContainer.length) {
+ const batchPath = filesContainer.data('diffFilesPath');
+
+ axios
+ .get(batchPath)
+ .then(({ data }) => {
+ filesContainer.html($(data.html));
+ syntaxHighlight(filesContainer);
+ handleLocationHash();
+ initAfterPageLoad();
+ })
+ .catch(() => {
+ flash(__('An error occurred while retrieving diff files'));
+ });
+ } else {
+ initAfterPageLoad();
+ }
});
diff --git a/app/assets/javascripts/repository/components/tree_content.vue b/app/assets/javascripts/repository/components/tree_content.vue
index 721cc6787dc..702df42d655 100644
--- a/app/assets/javascripts/repository/components/tree_content.vue
+++ b/app/assets/javascripts/repository/components/tree_content.vue
@@ -1,4 +1,5 @@
<script>
+import { GlButton } from '@gitlab/ui';
import createFlash from '~/flash';
import { __ } from '../../locale';
import FileTable from './table/index.vue';
@@ -8,12 +9,15 @@ import projectPathQuery from '../queries/project_path.query.graphql';
import FilePreview from './preview/index.vue';
import { readmeFile } from '../utils/readme';
+const LIMIT = 1000;
const PAGE_SIZE = 100;
+export const INITIAL_FETCH_COUNT = LIMIT / PAGE_SIZE;
export default {
components: {
FileTable,
FilePreview,
+ GlButton,
},
mixins: [getRefMixin],
apollo: {
@@ -43,12 +47,19 @@ export default {
blobs: [],
},
isLoadingFiles: false,
+ isOverLimit: false,
+ clickedShowMore: false,
+ pageSize: PAGE_SIZE,
+ fetchCounter: 0,
};
},
computed: {
readme() {
return readmeFile(this.entries.blobs);
},
+ hasShowMore() {
+ return !this.clickedShowMore && this.fetchCounter === INITIAL_FETCH_COUNT;
+ },
},
watch: {
@@ -76,7 +87,7 @@ export default {
ref: this.ref,
path: this.path || '/',
nextPageCursor: this.nextPageCursor,
- pageSize: PAGE_SIZE,
+ pageSize: this.pageSize,
},
})
.then(({ data }) => {
@@ -96,7 +107,11 @@ export default {
if (pageInfo?.hasNextPage) {
this.nextPageCursor = pageInfo.endCursor;
- this.fetchFiles();
+ this.fetchCounter += 1;
+ if (this.fetchCounter < INITIAL_FETCH_COUNT || this.clickedShowMore) {
+ this.fetchFiles();
+ this.clickedShowMore = false;
+ }
}
})
.catch(error => {
@@ -112,6 +127,10 @@ export default {
.concat(data.trees.pageInfo, data.submodules.pageInfo, data.blobs.pageInfo)
.find(({ hasNextPage }) => hasNextPage);
},
+ showMore() {
+ this.clickedShowMore = true;
+ this.fetchFiles();
+ },
},
};
</script>
@@ -124,6 +143,19 @@ export default {
:is-loading="isLoadingFiles"
:loading-path="loadingPath"
/>
+ <div
+ v-if="hasShowMore"
+ class="gl-border-1 gl-border-gray-100 gl-rounded-base gl-border-t-none gl-border-b-solid gl-border-l-solid gl-border-r-solid gl-rounded-top-right-none gl-rounded-top-left-none gl-mt-n1"
+ >
+ <gl-button
+ variant="link"
+ class="gl-display-flex gl-w-full gl-py-4!"
+ :loading="isLoadingFiles"
+ @click="showMore"
+ >
+ {{ s__('ProjectFileTree|Show more') }}
+ </gl-button>
+ </div>
<file-preview v-if="readme" :blob="readme" />
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
index 7dbeb3055c6..c6f7d5e44ad 100644
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
@@ -1,6 +1,6 @@
<script>
-import { mapState, mapActions } from 'vuex';
-import { __ } from '~/locale';
+import { mapState } from 'vuex';
+import { __, sprintf } from '~/locale';
import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '~/sidebar/event_hub';
@@ -23,9 +23,10 @@ export default {
required: true,
type: Boolean,
},
- service: {
- required: true,
- type: Object,
+ issuableType: {
+ required: false,
+ type: String,
+ default: 'issue',
},
},
data() {
@@ -34,13 +35,25 @@ export default {
};
},
computed: {
- ...mapState({ confidential: ({ noteableData }) => noteableData.confidential }),
+ ...mapState({
+ confidential: ({ noteableData, confidential }) => {
+ if (noteableData) {
+ return noteableData.confidential;
+ }
+ return Boolean(confidential);
+ },
+ }),
confidentialityIcon() {
return this.confidential ? 'eye-slash' : 'eye';
},
tooltipLabel() {
return this.confidential ? __('Confidential') : __('Not confidential');
},
+ confidentialText() {
+ return sprintf(__('This %{issuableType} is confidential'), {
+ issuableType: this.issuableType,
+ });
+ },
},
created() {
eventHub.$on('closeConfidentialityForm', this.toggleForm);
@@ -49,7 +62,6 @@ export default {
eventHub.$off('closeConfidentialityForm', this.toggleForm);
},
methods: {
- ...mapActions(['setConfidentiality']),
toggleForm() {
this.edit = !this.edit;
},
@@ -86,7 +98,12 @@ export default {
>
</div>
<div class="value sidebar-item-value hide-collapsed">
- <edit-form v-if="edit" :is-confidential="confidential" :full-path="fullPath" />
+ <edit-form
+ v-if="edit"
+ :confidential="confidential"
+ :full-path="fullPath"
+ :issuable-type="issuableType"
+ />
<div v-if="!confidential" class="no-value sidebar-item-value" data-testid="not-confidential">
<icon :size="16" name="eye" aria-hidden="true" class="sidebar-item-icon inline" />
{{ __('Not confidential') }}
@@ -98,7 +115,7 @@ export default {
aria-hidden="true"
class="sidebar-item-icon inline is-active"
/>
- {{ __('This issue is confidential') }}
+ {{ confidentialText }}
</div>
</div>
</div>
diff --git a/app/assets/javascripts/sidebar/components/confidential/edit_form.vue b/app/assets/javascripts/sidebar/components/confidential/edit_form.vue
index 9dd4f04acdb..6dad68800f9 100644
--- a/app/assets/javascripts/sidebar/components/confidential/edit_form.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/edit_form.vue
@@ -1,13 +1,13 @@
<script>
import editFormButtons from './edit_form_buttons.vue';
-import { s__ } from '../../../locale';
+import { __, sprintf } from '../../../locale';
export default {
components: {
editFormButtons,
},
props: {
- isConfidential: {
+ confidential: {
required: true,
type: Boolean,
},
@@ -15,16 +15,32 @@ export default {
required: true,
type: String,
},
+ issuableType: {
+ required: true,
+ type: String,
+ },
},
computed: {
confidentialityOnWarning() {
- return s__(
- 'confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue.',
+ const accessLevel = __('at least Reporter access');
+
+ return sprintf(
+ __(
+ 'You are going to turn on the confidentiality. This means that only team members with %{accessLevel} are able to see and leave comments on the %{issuableType}.',
+ ),
+ { issuableType: this.issuableType, accessLevel: `<strong>${accessLevel}</strong>` },
+ false,
);
},
confidentialityOffWarning() {
- return s__(
- 'confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue.',
+ const accessLevel = __('everyone');
+
+ return sprintf(
+ __(
+ 'You are going to turn off the confidentiality. This means %{accessLevel} will be able to see and leave a comment on this %{issuableType}.',
+ ),
+ { issuableType: this.issuableType, accessLevel: `<strong>${accessLevel}</strong>` },
+ false,
);
},
},
@@ -35,9 +51,9 @@ export default {
<div class="dropdown show">
<div class="dropdown-menu sidebar-item-warning-message">
<div>
- <p v-if="!isConfidential" v-html="confidentialityOnWarning"></p>
+ <p v-if="!confidential" v-html="confidentialityOnWarning"></p>
<p v-else v-html="confidentialityOffWarning"></p>
- <edit-form-buttons :full-path="fullPath" />
+ <edit-form-buttons :full-path="fullPath" :confidential="confidential" />
</div>
</div>
</div>
diff --git a/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue b/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue
index ef3e0ccfa86..be273a616a0 100644
--- a/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue
@@ -1,7 +1,7 @@
<script>
import $ from 'jquery';
import { GlLoadingIcon } from '@gitlab/ui';
-import { mapActions, mapState } from 'vuex';
+import { mapActions } from 'vuex';
import { __ } from '~/locale';
import Flash from '~/flash';
import eventHub from '../../event_hub';
@@ -15,6 +15,10 @@ export default {
required: true,
type: String,
},
+ confidential: {
+ required: true,
+ type: Boolean,
+ },
},
data() {
return {
@@ -22,7 +26,6 @@ export default {
};
},
computed: {
- ...mapState({ confidential: ({ noteableData }) => noteableData.confidential }),
toggleButtonText() {
if (this.isLoading) {
return __('Applying');
@@ -32,7 +35,7 @@ export default {
},
},
methods: {
- ...mapActions(['updateConfidentialityOnIssue']),
+ ...mapActions(['updateConfidentialityOnIssuable']),
closeForm() {
eventHub.$emit('closeConfidentialityForm');
$(this.$el).trigger('hidden.gl.dropdown');
@@ -41,9 +44,14 @@ export default {
this.isLoading = true;
const confidential = !this.confidential;
- this.updateConfidentialityOnIssue({ confidential, fullPath: this.fullPath })
- .catch(() => {
- Flash(__('Something went wrong trying to change the confidentiality of this issue'));
+ this.updateConfidentialityOnIssuable({ confidential, fullPath: this.fullPath })
+ .then(() => {
+ eventHub.$emit('updateIssuableConfidentiality', confidential);
+ })
+ .catch(err => {
+ Flash(
+ err || __('Something went wrong trying to change the confidentiality of this issue'),
+ );
})
.finally(() => {
this.closeForm();
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index 1c88883142d..9f9964ac447 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -165,9 +165,8 @@
vertical-align: top;
@media(max-width: map-get($grid-breakpoints, lg)-1) {
- display: block;
+ display: inline-block;
width: 100%;
- margin: 5px 0;
padding: 0;
border-left: 0;
}
diff --git a/app/controllers/groups/releases_controller.rb b/app/controllers/groups/releases_controller.rb
new file mode 100644
index 00000000000..500c57a6f3e
--- /dev/null
+++ b/app/controllers/groups/releases_controller.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Groups
+ class ReleasesController < Groups::ApplicationController
+ def index
+ respond_to do |format|
+ format.json do
+ render json: ReleaseSerializer.new.represent(releases)
+ end
+ end
+ end
+
+ private
+
+ def releases
+ ReleasesFinder
+ .new(@group, current_user, { include_subgroups: true })
+ .execute(preload: false)
+ .page(params[:page])
+ .per(30)
+ end
+ end
+end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 3f2dc9b09fa..b0c6f3cc6a1 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -15,8 +15,8 @@ class Projects::CommitController < Projects::ApplicationController
before_action :authorize_download_code!
before_action :authorize_read_pipeline!, only: [:pipelines]
before_action :commit
- before_action :define_commit_vars, only: [:show, :diff_for_path, :pipelines, :merge_requests]
- before_action :define_note_vars, only: [:show, :diff_for_path]
+ before_action :define_commit_vars, only: [:show, :diff_for_path, :diff_files, :pipelines, :merge_requests]
+ before_action :define_note_vars, only: [:show, :diff_for_path, :diff_files]
before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
BRANCH_SEARCH_LIMIT = 1000
@@ -41,6 +41,10 @@ class Projects::CommitController < Projects::ApplicationController
render_diff_for_path(@commit.diffs(diff_options))
end
+ def diff_files
+ render json: { html: view_to_html_string('projects/commit/diff_files', diffs: @diffs, environment: @environment) }
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def pipelines
@pipelines = @commit.pipelines.order(id: :desc)
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 5076172985d..8d498af8de9 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -15,6 +15,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
push_frontend_feature_flag(:prometheus_computed_alerts)
push_frontend_feature_flag(:disable_metric_dashboard_refresh_rate)
push_frontend_feature_flag(:alert_runbooks)
+ push_frontend_feature_flag(:metrics_dashboard_new_panel_page)
end
before_action :authorize_read_environment!, except: [:metrics, :additional_metrics, :metrics_dashboard, :metrics_redirect]
before_action :authorize_create_environment!, only: [:new, :create]
diff --git a/app/controllers/projects/metrics_dashboard_controller.rb b/app/controllers/projects/metrics_dashboard_controller.rb
index ff60d069061..595db86c5fe 100644
--- a/app/controllers/projects/metrics_dashboard_controller.rb
+++ b/app/controllers/projects/metrics_dashboard_controller.rb
@@ -11,6 +11,7 @@ module Projects
push_frontend_feature_flag(:prometheus_computed_alerts)
push_frontend_feature_flag(:disable_metric_dashboard_refresh_rate)
push_frontend_feature_flag(:alert_runbooks)
+ push_frontend_feature_flag(:metrics_dashboard_new_panel_page)
end
def show
diff --git a/app/finders/releases_finder.rb b/app/finders/releases_finder.rb
index 6a754fdb5a1..e961ad4c0ca 100644
--- a/app/finders/releases_finder.rb
+++ b/app/finders/releases_finder.rb
@@ -1,19 +1,20 @@
# frozen_string_literal: true
class ReleasesFinder
- attr_reader :project, :current_user, :params
+ include Gitlab::Utils::StrongMemoize
- def initialize(project, current_user = nil, params = {})
- @project = project
+ attr_reader :parent, :current_user, :params
+
+ def initialize(parent, current_user = nil, params = {})
+ @parent = parent
@current_user = current_user
@params = params
end
def execute(preload: true)
- return Release.none unless Ability.allowed?(current_user, :read_release, project)
+ return Release.none if projects.empty?
- # See https://gitlab.com/gitlab-org/gitlab/-/issues/211988
- releases = project.releases.where.not(tag: nil) # rubocop:disable CodeReuse/ActiveRecord
+ releases = get_releases
releases = by_tag(releases)
releases = releases.preloaded if preload
releases.sorted
@@ -21,6 +22,34 @@ class ReleasesFinder
private
+ def get_releases
+ Release.where(project_id: projects).where.not(tag: nil) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ def include_subgroups?
+ params.fetch(:include_subgroups, false)
+ end
+
+ def projects
+ strong_memoize(:projects) do
+ if parent.is_a?(Project)
+ Ability.allowed?(current_user, :read_release, parent) ? [parent] : []
+ elsif parent.is_a?(Group)
+ accessible_projects
+ end
+ end
+ end
+
+ def accessible_projects
+ projects = if include_subgroups?
+ Project.for_group_and_its_subgroups(parent)
+ else
+ parent.projects
+ end
+
+ projects.select { |project| Ability.allowed?(current_user, :read_release, project) }
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def by_tag(releases)
return releases unless params[:tag].present?
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 1b9876b9a6a..377aee1ae9e 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -229,6 +229,7 @@ module SearchHelper
opts[:data]['group-id'] = @group.id
opts[:data]['labels-endpoint'] = group_labels_path(@group)
opts[:data]['milestones-endpoint'] = group_milestones_path(@group)
+ opts[:data]['releases-endpoint'] = group_releases_path(@group)
else
opts[:data]['labels-endpoint'] = dashboard_labels_path
opts[:data]['milestones-endpoint'] = dashboard_milestones_path
diff --git a/app/models/ci/pipeline_artifact.rb b/app/models/ci/pipeline_artifact.rb
index d82b8ade617..76795561b53 100644
--- a/app/models/ci/pipeline_artifact.rb
+++ b/app/models/ci/pipeline_artifact.rb
@@ -17,7 +17,7 @@ module Ci
belongs_to :project, class_name: "Project", inverse_of: :pipeline_artifacts
belongs_to :pipeline, class_name: "Ci::Pipeline", inverse_of: :pipeline_artifacts
- validates :pipeline, :project, :file_format, presence: true
+ validates :pipeline, :project, :file_format, :file, presence: true
validates :file_store, presence: true, inclusion: { in: FILE_STORE_SUPPORTED }
validates :size, presence: true, numericality: { less_than_or_equal_to: FILE_SIZE_LIMIT }
validates :file_type, presence: true, uniqueness: { scope: [:pipeline_id] }
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index fe51bdc2486..dd5aedbb760 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -61,6 +61,8 @@ module Issuable
end
end
+ has_many :note_authors, -> { distinct }, through: :notes, source: :author
+
has_many :label_links, as: :target, dependent: :destroy, inverse_of: :target # rubocop:disable Cop/ActiveRecordDependent
has_many :labels, through: :label_links
has_many :todos, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
diff --git a/app/serializers/release_entity.rb b/app/serializers/release_entity.rb
new file mode 100644
index 00000000000..6777b0f9780
--- /dev/null
+++ b/app/serializers/release_entity.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class ReleaseEntity < Grape::Entity
+ expose :id
+ expose :tag # see https://gitlab.com/gitlab-org/gitlab/-/issues/36338
+end
diff --git a/app/serializers/release_serializer.rb b/app/serializers/release_serializer.rb
new file mode 100644
index 00000000000..05a13f71a6f
--- /dev/null
+++ b/app/serializers/release_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ReleaseSerializer < BaseSerializer
+ entity ReleaseEntity
+end
diff --git a/app/services/metrics/dashboard/base_service.rb b/app/services/metrics/dashboard/base_service.rb
index f7397110c13..311c7f23f16 100644
--- a/app/services/metrics/dashboard/base_service.rb
+++ b/app/services/metrics/dashboard/base_service.rb
@@ -13,7 +13,6 @@ module Metrics
STAGES::MetricEndpointInserter,
STAGES::VariableEndpointInserter,
STAGES::PanelIdsInserter,
- STAGES::Sorter,
STAGES::AlertsInserter,
STAGES::UrlValidator
].freeze
diff --git a/app/services/metrics/dashboard/clone_dashboard_service.rb b/app/services/metrics/dashboard/clone_dashboard_service.rb
index b5df82ea92a..d9bd9423a1b 100644
--- a/app/services/metrics/dashboard/clone_dashboard_service.rb
+++ b/app/services/metrics/dashboard/clone_dashboard_service.rb
@@ -13,8 +13,7 @@ module Metrics
SEQUENCES = {
::Metrics::Dashboard::SystemDashboardService::DASHBOARD_PATH => [
::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter,
- ::Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter,
- ::Gitlab::Metrics::Dashboard::Stages::Sorter
+ ::Gitlab::Metrics::Dashboard::Stages::CustomMetricsInserter
].freeze,
::Metrics::Dashboard::SelfMonitoringDashboardService::DASHBOARD_PATH => [
@@ -22,8 +21,7 @@ module Metrics
].freeze,
::Metrics::Dashboard::ClusterDashboardService::DASHBOARD_PATH => [
- ::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter,
- ::Gitlab::Metrics::Dashboard::Stages::Sorter
+ ::Gitlab::Metrics::Dashboard::Stages::CommonMetricsInserter
].freeze
}.freeze
diff --git a/app/services/metrics/dashboard/cluster_dashboard_service.rb b/app/services/metrics/dashboard/cluster_dashboard_service.rb
index 49b9864b66c..4a28e847fdd 100644
--- a/app/services/metrics/dashboard/cluster_dashboard_service.rb
+++ b/app/services/metrics/dashboard/cluster_dashboard_service.rb
@@ -13,8 +13,7 @@ module Metrics
SEQUENCE = [
STAGES::ClusterEndpointInserter,
- STAGES::PanelIdsInserter,
- STAGES::Sorter
+ STAGES::PanelIdsInserter
].freeze
class << self
diff --git a/app/services/metrics/dashboard/custom_metric_embed_service.rb b/app/services/metrics/dashboard/custom_metric_embed_service.rb
index 22b592c7aa5..229bd17f5cf 100644
--- a/app/services/metrics/dashboard/custom_metric_embed_service.rb
+++ b/app/services/metrics/dashboard/custom_metric_embed_service.rb
@@ -75,7 +75,6 @@ module Metrics
def panels
[{
type: DEFAULT_PANEL_TYPE,
- weight: DEFAULT_PANEL_WEIGHT,
title: title,
y_label: y_label,
metrics: metrics.map(&:to_metric_hash)
diff --git a/app/services/metrics/dashboard/pod_dashboard_service.rb b/app/services/metrics/dashboard/pod_dashboard_service.rb
index 310f78c6a4b..c83f8618460 100644
--- a/app/services/metrics/dashboard/pod_dashboard_service.rb
+++ b/app/services/metrics/dashboard/pod_dashboard_service.rb
@@ -12,8 +12,7 @@ module Metrics
SEQUENCE = [
STAGES::MetricEndpointInserter,
STAGES::VariableEndpointInserter,
- STAGES::PanelIdsInserter,
- STAGES::Sorter
+ STAGES::PanelIdsInserter
].freeze
class << self
diff --git a/app/services/metrics/dashboard/predefined_dashboard_service.rb b/app/services/metrics/dashboard/predefined_dashboard_service.rb
index 40925a604ff..abdef66c2e0 100644
--- a/app/services/metrics/dashboard/predefined_dashboard_service.rb
+++ b/app/services/metrics/dashboard/predefined_dashboard_service.rb
@@ -12,8 +12,7 @@ module Metrics
SEQUENCE = [
STAGES::MetricEndpointInserter,
STAGES::VariableEndpointInserter,
- STAGES::PanelIdsInserter,
- STAGES::Sorter
+ STAGES::PanelIdsInserter
].freeze
class << self
diff --git a/app/services/metrics/dashboard/self_monitoring_dashboard_service.rb b/app/services/metrics/dashboard/self_monitoring_dashboard_service.rb
index eb0575c9d23..0651e569d07 100644
--- a/app/services/metrics/dashboard/self_monitoring_dashboard_service.rb
+++ b/app/services/metrics/dashboard/self_monitoring_dashboard_service.rb
@@ -15,8 +15,7 @@ module Metrics
STAGES::CustomMetricsInserter,
STAGES::MetricEndpointInserter,
STAGES::VariableEndpointInserter,
- STAGES::PanelIdsInserter,
- STAGES::Sorter
+ STAGES::PanelIdsInserter
].freeze
class << self
diff --git a/app/services/metrics/dashboard/system_dashboard_service.rb b/app/services/metrics/dashboard/system_dashboard_service.rb
index 9560df00e4a..29b8f23f40d 100644
--- a/app/services/metrics/dashboard/system_dashboard_service.rb
+++ b/app/services/metrics/dashboard/system_dashboard_service.rb
@@ -18,7 +18,6 @@ module Metrics
STAGES::MetricEndpointInserter,
STAGES::VariableEndpointInserter,
STAGES::PanelIdsInserter,
- STAGES::Sorter,
STAGES::AlertsInserter
].freeze
diff --git a/app/views/projects/commit/diff_files.html.haml b/app/views/projects/commit/diff_files.html.haml
new file mode 100644
index 00000000000..3a473be3840
--- /dev/null
+++ b/app/views/projects/commit/diff_files.html.haml
@@ -0,0 +1,3 @@
+- diff_files = diffs.diff_files
+
+= render partial: 'projects/diffs/file', collection: diff_files, as: :diff_file, locals: { project: diffs.project, environment: environment, diff_page_context: 'is-commit' }
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index 4b76dde681e..6ba363e6555 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -3,6 +3,7 @@
- can_create_note = !@diff_notes_disabled && can?(current_user, :create_note, diffs.project)
- diff_files = diffs.diff_files
- diff_page_context = local_assigns.fetch(:diff_page_context, nil)
+- load_diff_files_async = Feature.enabled?(:async_commit_diff_files, @project) && diff_page_context == "is-commit"
.content-block.oneline-block.files-changed.diff-files-changed.js-diff-files-changed
.files-changed-inner
@@ -26,5 +27,12 @@
- if render_overflow_warning?(diffs)
= render 'projects/diffs/warning', diff_files: diffs
+
.files{ data: { can_create_note: can_create_note } }
- = render partial: 'projects/diffs/file', collection: diff_files, as: :diff_file, locals: { project: diffs.project, environment: environment, diff_page_context: diff_page_context }
+ - if load_diff_files_async
+ - url = url_for(safe_params.merge(action: 'diff_files'))
+ .js-diffs-batch{ data: { diff_files_path: url } }
+ .text-center
+ %span.spinner.spinner-md
+ - else
+ = render partial: 'projects/diffs/file', collection: diff_files, as: :diff_file, locals: { project: diffs.project, environment: environment, diff_page_context: diff_page_context }
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index d17fc5e2eef..20473b47484 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -113,18 +113,17 @@
- if member.can_remove?
- if current_user == user
- = link_to icon('sign-out', text: _('Leave')), polymorphic_path([:leave, member.source, :members]),
- method: :delete,
- data: { confirm: leave_confirmation_message(member.source) },
- class: "btn btn-remove align-self-center m-0 #{'ml-sm-2' unless force_mobile_view}"
+ = link_to polymorphic_path([:leave, member.source, :members]), method: :delete, data: { confirm: leave_confirmation_message(member.source) }, class: "btn gl-button btn-svg btn-danger align-self-center m-0 #{'ml-sm-2' unless force_mobile_view}" do
+ = sprite_icon('leave', css_class: 'gl-icon')
+ = _('Leave')
- else
%button{ data: { member_path: member_path(member.member), message: remove_member_message(member), is_access_request: member.request?.to_s, qa_selector: 'delete_member_button' },
- class: "js-remove-member-button btn btn-remove align-self-center m-0 #{'ml-sm-2' unless force_mobile_view}",
+ class: "js-remove-member-button btn gl-button btn-danger align-self-center m-0 #{'ml-sm-2 btn-icon' unless force_mobile_view}",
title: remove_member_title(member) }
%span{ class: ('d-block d-sm-none' unless force_mobile_view) }
= _("Delete")
- unless force_mobile_view
- = icon('trash', class: 'd-none d-sm-block')
+ = sprite_icon('remove', css_class: 'd-none d-sm-block gl-icon')
= render_if_exists 'shared/members/ee/override_member_buttons', group: @group, member: member, user: user, action: :edit, can_override: member.can_override?
- else
%span.member-access-text.user-access-role= member.human_access