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-05-20 00:08:05 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-20 00:08:05 +0300
commit680d18802596089dc407b7011bcf682d24846aec (patch)
tree09e1beea15fe9ba9d1a757c31b7836e5f7e9fa89 /app
parentd84f18d66c1fc46f244b0f4dec8bf65b90d9882a (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/clusters_list/components/clusters.vue92
-rw-r--r--app/assets/javascripts/clusters_list/store/actions.js16
-rw-r--r--app/assets/javascripts/clusters_list/store/mutation_types.js1
-rw-r--r--app/assets/javascripts/clusters_list/store/mutations.js7
-rw-r--r--app/assets/javascripts/clusters_list/store/state.js3
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue19
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue8
-rw-r--r--app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue2
-rw-r--r--app/policies/project_policy.rb2
-rw-r--r--app/services/snippets/base_service.rb24
-rw-r--r--app/services/snippets/create_service.rb30
-rw-r--r--app/services/snippets/update_service.rb19
-rw-r--r--app/views/shared/access_tokens/_table.html.haml2
-rw-r--r--app/views/users/calendar_activities.html.haml4
14 files changed, 154 insertions, 75 deletions
diff --git a/app/assets/javascripts/clusters_list/components/clusters.vue b/app/assets/javascripts/clusters_list/components/clusters.vue
index eb575b9ed6c..af3f1437c64 100644
--- a/app/assets/javascripts/clusters_list/components/clusters.vue
+++ b/app/assets/javascripts/clusters_list/components/clusters.vue
@@ -1,22 +1,32 @@
<script>
import { mapState, mapActions } from 'vuex';
-import { GlTable, GlLink, GlLoadingIcon, GlBadge } from '@gitlab/ui';
+import { GlBadge, GlLink, GlLoadingIcon, GlPagination, GlTable } from '@gitlab/ui';
import tooltip from '~/vue_shared/directives/tooltip';
import { CLUSTER_TYPES, STATUSES } from '../constants';
import { __, sprintf } from '~/locale';
export default {
components: {
- GlTable,
+ GlBadge,
GlLink,
GlLoadingIcon,
- GlBadge,
+ GlPagination,
+ GlTable,
},
directives: {
tooltip,
},
computed: {
- ...mapState(['clusters', 'loading']),
+ ...mapState(['clusters', 'clustersPerPage', 'loading', 'page', 'totalCulsters']),
+ currentPage: {
+ get() {
+ return this.page;
+ },
+ set(newVal) {
+ this.setPage(newVal);
+ this.fetchClusters();
+ },
+ },
fields() {
return [
{
@@ -47,12 +57,15 @@ export default {
},
];
},
+ hasClusters() {
+ return this.clustersPerPage > 0;
+ },
},
mounted() {
this.fetchClusters();
},
methods: {
- ...mapActions(['fetchClusters']),
+ ...mapActions(['fetchClusters', 'setPage']),
statusClass(status) {
const iconClass = STATUSES[status] || STATUSES.default;
return iconClass.className;
@@ -67,33 +80,46 @@ export default {
<template>
<gl-loading-icon v-if="loading" size="md" class="mt-3" />
- <gl-table v-else :items="clusters" :fields="fields" stacked="md" class="qa-clusters-table">
- <template #cell(name)="{ item }">
- <div class="d-flex flex-row-reverse flex-md-row js-status">
- <gl-link data-qa-selector="cluster" :data-qa-cluster-name="item.name" :href="item.path">
- {{ item.name }}
- </gl-link>
- <gl-loading-icon
- v-if="item.status === 'deleting'"
- v-tooltip
- :title="statusTitle(item.status)"
- size="sm"
- class="mr-2 ml-md-2"
- />
- <div
- v-else
- v-tooltip
- class="cluster-status-indicator rounded-circle align-self-center gl-w-4 gl-h-4 mr-2 ml-md-2"
- :class="statusClass(item.status)"
- :title="statusTitle(item.status)"
- ></div>
- </div>
- </template>
- <template #cell(cluster_type)="{value}">
- <gl-badge variant="light">
- {{ value }}
- </gl-badge>
- </template>
- </gl-table>
+ <section v-else>
+ <gl-table :items="clusters" :fields="fields" stacked="md" class="qa-clusters-table">
+ <template #cell(name)="{ item }">
+ <div class="d-flex flex-row-reverse flex-md-row js-status">
+ <gl-link data-qa-selector="cluster" :data-qa-cluster-name="item.name" :href="item.path">
+ {{ item.name }}
+ </gl-link>
+
+ <gl-loading-icon
+ v-if="item.status === 'deleting'"
+ v-tooltip
+ :title="statusTitle(item.status)"
+ size="sm"
+ class="mr-2 ml-md-2"
+ />
+ <div
+ v-else
+ v-tooltip
+ class="cluster-status-indicator rounded-circle align-self-center gl-w-4 gl-h-4 mr-2 ml-md-2"
+ :class="statusClass(item.status)"
+ :title="statusTitle(item.status)"
+ ></div>
+ </div>
+ </template>
+ <template #cell(cluster_type)="{value}">
+ <gl-badge variant="light">
+ {{ value }}
+ </gl-badge>
+ </template>
+ </gl-table>
+
+ <gl-pagination
+ v-if="hasClusters"
+ v-model="currentPage"
+ :per-page="clustersPerPage"
+ :total-items="totalCulsters"
+ :prev-text="__('Prev')"
+ :next-text="__('Next')"
+ align="center"
+ />
+ </section>
</template>
diff --git a/app/assets/javascripts/clusters_list/store/actions.js b/app/assets/javascripts/clusters_list/store/actions.js
index d0ad92f5536..919625f69b4 100644
--- a/app/assets/javascripts/clusters_list/store/actions.js
+++ b/app/assets/javascripts/clusters_list/store/actions.js
@@ -2,18 +2,22 @@ import Poll from '~/lib/utils/poll';
import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
import { __ } from '~/locale';
+import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import * as types from './mutation_types';
export const fetchClusters = ({ state, commit }) => {
const poll = new Poll({
resource: {
- fetchClusters: endpoint => axios.get(endpoint),
+ fetchClusters: paginatedEndPoint => axios.get(paginatedEndPoint),
},
- data: state.endpoint,
+ data: `${state.endpoint}?page=${state.page}`,
method: 'fetchClusters',
- successCallback: ({ data }) => {
+ successCallback: ({ data, headers }) => {
if (data.clusters) {
- commit(types.SET_CLUSTERS_DATA, data);
+ const normalizedHeaders = normalizeHeaders(headers);
+ const paginationInformation = parseIntPagination(normalizedHeaders);
+
+ commit(types.SET_CLUSTERS_DATA, { data, paginationInformation });
commit(types.SET_LOADING_STATE, false);
poll.stop();
}
@@ -24,5 +28,9 @@ export const fetchClusters = ({ state, commit }) => {
poll.makeRequest();
};
+export const setPage = ({ commit }, page) => {
+ commit(types.SET_PAGE, page);
+};
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/clusters_list/store/mutation_types.js b/app/assets/javascripts/clusters_list/store/mutation_types.js
index f056f3ab7d9..a5275f28c13 100644
--- a/app/assets/javascripts/clusters_list/store/mutation_types.js
+++ b/app/assets/javascripts/clusters_list/store/mutation_types.js
@@ -1,2 +1,3 @@
export const SET_CLUSTERS_DATA = 'SET_CLUSTERS_DATA';
export const SET_LOADING_STATE = 'SET_LOADING_STATE';
+export const SET_PAGE = 'SET_PAGE';
diff --git a/app/assets/javascripts/clusters_list/store/mutations.js b/app/assets/javascripts/clusters_list/store/mutations.js
index ce53a033628..2a9df9f38f0 100644
--- a/app/assets/javascripts/clusters_list/store/mutations.js
+++ b/app/assets/javascripts/clusters_list/store/mutations.js
@@ -4,10 +4,15 @@ export default {
[types.SET_LOADING_STATE](state, value) {
state.loading = value;
},
- [types.SET_CLUSTERS_DATA](state, data) {
+ [types.SET_CLUSTERS_DATA](state, { data, paginationInformation }) {
Object.assign(state, {
clusters: data.clusters,
+ clustersPerPage: paginationInformation.perPage,
hasAncestorClusters: data.has_ancestor_clusters,
+ totalCulsters: paginationInformation.total,
});
},
+ [types.SET_PAGE](state, value) {
+ state.page = Number(value) || 1;
+ },
};
diff --git a/app/assets/javascripts/clusters_list/store/state.js b/app/assets/javascripts/clusters_list/store/state.js
index 31e73558c2e..d590ea09e66 100644
--- a/app/assets/javascripts/clusters_list/store/state.js
+++ b/app/assets/javascripts/clusters_list/store/state.js
@@ -3,4 +3,7 @@ export default (initialState = {}) => ({
hasAncestorClusters: false,
loading: true,
clusters: [],
+ clustersPerPage: 0,
+ page: 1,
+ totalCulsters: 0,
});
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index a323e08d26d..81812ee2279 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -1,5 +1,6 @@
<script>
import { mapActions } from 'vuex';
+import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
import timeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
export default {
@@ -7,6 +8,10 @@ export default {
timeAgoTooltip,
GitlabTeamMemberBadge: () =>
import('ee_component/vue_shared/components/user_avatar/badges/gitlab_team_member_badge.vue'),
+ GlIcon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
},
props: {
author: {
@@ -44,6 +49,11 @@ export default {
required: false,
default: true,
},
+ isConfidential: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -174,6 +184,15 @@ export default {
</a>
<time-ago-tooltip v-else ref="noteTimestamp" :time="createdAt" tooltip-placement="bottom" />
</template>
+ <gl-icon
+ v-if="isConfidential"
+ v-gl-tooltip:tooltipcontainer.bottom
+ data-testid="confidentialIndicator"
+ name="eye-slash"
+ :size="14"
+ :title="s__('Notes|Private comments are accessible by internal staff only')"
+ class="gl-ml-1 gl-text-gray-800 align-middle"
+ />
<slot name="extra-controls"></slot>
<i
v-if="showSpinner"
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index a74207403e1..37675e20b3d 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -255,7 +255,13 @@ export default {
</div>
<div class="timeline-content">
<div class="note-header">
- <note-header v-once :author="author" :created-at="note.created_at" :note-id="note.id">
+ <note-header
+ v-once
+ :author="author"
+ :created-at="note.created_at"
+ :note-id="note.id"
+ :is-confidential="note.confidential"
+ >
<slot slot="note-header-info" name="note-header-info"></slot>
<span v-if="commit" v-html="actionText"></span>
<span v-else-if="note.created_at" class="d-none d-sm-inline">&middot;</span>
diff --git a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
index 26adf4cbbe0..e18732d0fd5 100644
--- a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
+++ b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
@@ -68,7 +68,7 @@ export default {
footer-primary-button-variant="warning"
@submit="onSubmit"
>
- <template slot="title">
+ <template #title>
{{ title }}
</template>
<div>
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 50443048274..a24c0471d6c 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -316,6 +316,7 @@ class ProjectPolicy < BasePolicy
enable :update_deployment
enable :create_release
enable :update_release
+ enable :daily_statistics
enable :create_metrics_dashboard_annotation
enable :delete_metrics_dashboard_annotation
enable :update_metrics_dashboard_annotation
@@ -358,7 +359,6 @@ class ProjectPolicy < BasePolicy
enable :create_environment_terminal
enable :destroy_release
enable :destroy_artifacts
- enable :daily_statistics
enable :admin_operations
enable :read_deploy_token
enable :create_deploy_token
diff --git a/app/services/snippets/base_service.rb b/app/services/snippets/base_service.rb
index c8215e79c56..81d12997335 100644
--- a/app/services/snippets/base_service.rb
+++ b/app/services/snippets/base_service.rb
@@ -2,8 +2,32 @@
module Snippets
class BaseService < ::BaseService
+ include SpamCheckMethods
+
+ CreateRepositoryError = Class.new(StandardError)
+
+ attr_reader :uploaded_files
+
+ def initialize(project, user = nil, params = {})
+ super
+
+ @uploaded_files = Array(@params.delete(:files).presence)
+
+ filter_spam_check_params
+ end
+
private
+ def visibility_allowed?(snippet, visibility_level)
+ Gitlab::VisibilityLevel.allowed_for?(current_user, visibility_level)
+ end
+
+ def error_forbidden_visibility(snippet)
+ deny_visibility_level(snippet)
+
+ snippet_error_response(snippet, 403)
+ end
+
def snippet_error_response(snippet, http_status)
ServiceResponse.error(
message: snippet.errors.full_messages.to_sentence,
diff --git a/app/services/snippets/create_service.rb b/app/services/snippets/create_service.rb
index bb58d1bc2bc..ed6da3a0ad0 100644
--- a/app/services/snippets/create_service.rb
+++ b/app/services/snippets/create_service.rb
@@ -2,25 +2,11 @@
module Snippets
class CreateService < Snippets::BaseService
- include SpamCheckMethods
-
- CreateRepositoryError = Class.new(StandardError)
-
def execute
- filter_spam_check_params
-
- @files = Array(params.delete(:files).presence)
-
- @snippet = if project
- project.snippets.build(params)
- else
- PersonalSnippet.new(params)
- end
-
- unless Gitlab::VisibilityLevel.allowed_for?(current_user, @snippet.visibility_level)
- deny_visibility_level(@snippet)
+ @snippet = build_from_params
- return snippet_error_response(@snippet, 403)
+ unless visibility_allowed?(@snippet, @snippet.visibility_level)
+ return error_forbidden_visibility(@snippet)
end
@snippet.author = current_user
@@ -41,6 +27,14 @@ module Snippets
private
+ def build_from_params
+ if project
+ project.snippets.build(params)
+ else
+ PersonalSnippet.new(params)
+ end
+ end
+
def save_and_commit
snippet_saved = @snippet.save
@@ -91,7 +85,7 @@ module Snippets
def move_temporary_files
return unless @snippet.is_a?(PersonalSnippet)
- @files.each do |file|
+ uploaded_files.each do |file|
FileMover.new(file, from_model: current_user, to_model: @snippet).execute
end
end
diff --git a/app/services/snippets/update_service.rb b/app/services/snippets/update_service.rb
index e12610135c9..2dc9266dbd0 100644
--- a/app/services/snippets/update_service.rb
+++ b/app/services/snippets/update_service.rb
@@ -2,26 +2,15 @@
module Snippets
class UpdateService < Snippets::BaseService
- include SpamCheckMethods
-
COMMITTABLE_ATTRIBUTES = %w(file_name content).freeze
UpdateError = Class.new(StandardError)
- CreateRepositoryError = Class.new(StandardError)
def execute(snippet)
- # check that user is allowed to set specified visibility_level
- new_visibility = visibility_level
-
- if new_visibility && new_visibility.to_i != snippet.visibility_level
- unless Gitlab::VisibilityLevel.allowed_for?(current_user, new_visibility)
- deny_visibility_level(snippet, new_visibility)
-
- return snippet_error_response(snippet, 403)
- end
+ if visibility_changed?(snippet) && !visibility_allowed?(snippet, visibility_level)
+ return error_forbidden_visibility(snippet)
end
- filter_spam_check_params
snippet.assign_attributes(params)
spam_check(snippet, current_user)
@@ -36,6 +25,10 @@ module Snippets
private
+ def visibility_changed?(snippet)
+ visibility_level && visibility_level.to_i != snippet.visibility_level
+ end
+
def save_and_commit(snippet)
return false unless snippet.save
diff --git a/app/views/shared/access_tokens/_table.html.haml b/app/views/shared/access_tokens/_table.html.haml
index a05d0a2c006..5518c31cb06 100644
--- a/app/views/shared/access_tokens/_table.html.haml
+++ b/app/views/shared/access_tokens/_table.html.haml
@@ -27,7 +27,7 @@
%td
- if token.expires?
%span{ class: ('text-warning' if token.expires_soon?) }
- In #{distance_of_time_in_words_to_now(token.expires_at)}
+ = _('In %{time_to_now}') % { time_to_now: distance_of_time_in_words_to_now(token.expires_at) }
- else
%span.token-never-expires-label= _('Never')
%td= token.scopes.present? ? token.scopes.join(', ') : _('<no scopes selected>')
diff --git a/app/views/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml
index 7516dfe1602..a5197a9950b 100644
--- a/app/views/users/calendar_activities.html.haml
+++ b/app/views/users/calendar_activities.html.haml
@@ -22,14 +22,14 @@
- elsif event.target
= link_to event.target.to_reference, [event.project.namespace.becomes(Namespace), event.project, event.target], class: 'has-tooltip', title: event.target_title
- at
+ = s_('UserProfile|at')
%strong
- if event.project
= link_to_project(event.project)
- else
= event.resource_parent_name
- else
- made a private contribution
+ = s_('UserProfile|made a private contribution')
- else
%p
= _('No contributions were found')