提交 583fadea 编写于 作者: G GitLab Bot

Add latest changes from gitlab-org/gitlab@master

上级 05480e90
......@@ -232,7 +232,6 @@ export default {
class="flex-grow-1"
toggle-class="dropdown-menu-toggle"
:default-branch="defaultBranch"
:modal-id="$options.modalIds.duplicateDashboard"
@selectDashboard="selectDashboard"
/>
</div>
......@@ -429,6 +428,7 @@ export default {
<template v-if="isOutOfTheBoxDashboard">
<gl-new-dropdown-divider />
<gl-new-dropdown-item
ref="duplicateDashboardItem"
v-gl-modal="$options.modalIds.duplicateDashboard"
......@@ -436,6 +436,12 @@ export default {
>
{{ s__('Metrics|Duplicate current dashboard') }}
</gl-new-dropdown-item>
<duplicate-dashboard-modal
:default-branch="defaultBranch"
:modal-id="$options.modalIds.duplicateDashboard"
@dashboardDuplicated="selectDashboard"
/>
</template>
</gl-new-dropdown>
</div>
......@@ -450,10 +456,5 @@ export default {
/>
</div>
</div>
<duplicate-dashboard-modal
:default-branch="defaultBranch"
:modal-id="$options.modalIds.duplicateDashboard"
@dashboardDuplicated="selectDashboard"
/>
</div>
</template>
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
import { mapState, mapGetters } from 'vuex';
import {
GlIcon,
GlDeprecatedDropdown,
......@@ -31,10 +31,6 @@ export default {
type: String,
required: true,
},
modalId: {
type: String,
required: true,
},
},
data() {
return {
......@@ -44,9 +40,6 @@ export default {
computed: {
...mapState('monitoringDashboard', ['allDashboards']),
...mapGetters('monitoringDashboard', ['selectedDashboard']),
isOutOfTheBoxDashboard() {
return this.selectedDashboard?.out_of_the_box_dashboard;
},
selectedDashboardText() {
return this.selectedDashboard?.display_name;
},
......@@ -70,7 +63,6 @@ export default {
},
},
methods: {
...mapActions('monitoringDashboard', ['duplicateSystemDashboard']),
dashboardDisplayName(dashboard) {
return dashboard.display_name || dashboard.path || '';
},
......@@ -134,18 +126,6 @@ export default {
>
{{ __('No matching results') }}
</div>
<!--
This Duplicate Dashboard item will be removed from the dashboards dropdown
in https://gitlab.com/gitlab-org/gitlab/-/issues/223223
-->
<template v-if="isOutOfTheBoxDashboard">
<gl-deprecated-dropdown-divider />
<gl-deprecated-dropdown-item v-gl-modal="modalId" data-testid="duplicateDashboardItem">
{{ s__('Metrics|Duplicate dashboard') }}
</gl-deprecated-dropdown-item>
</template>
</div>
</gl-deprecated-dropdown>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import { GlDeprecatedButton, GlLoadingIcon } from '@gitlab/ui';
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
export default {
components: {
GlDeprecatedButton,
GlButton,
GlLoadingIcon,
},
props: {
......@@ -27,12 +27,12 @@ export default {
{{ __('Labels') }}
<template v-if="allowLabelEdit">
<gl-loading-icon v-show="labelsSelectInProgress" inline />
<gl-deprecated-button
<gl-button
variant="link"
class="pull-right js-sidebar-dropdown-toggle"
data-qa-selector="labels_edit_button"
@click="toggleDropdownContents"
>{{ __('Edit') }}</gl-deprecated-button
>{{ __('Edit') }}</gl-button
>
</template>
</div>
......
......@@ -8,6 +8,8 @@ module WikiActions
extend ActiveSupport::Concern
included do
before_action { respond_to :html }
before_action :authorize_read_wiki!
before_action :authorize_create_wiki!, only: [:edit, :create]
before_action :authorize_admin_wiki!, only: :destroy
......
---
title: Wiki controller should not crash with non-html format
merge_request: 37466
author:
type: fixed
---
title: Update label select vue gl button
merge_request: 37986
author:
type: changed
---
title: Skip mass unconfirming users when send_user_confirmation_email setting is off
merge_request: 38024
author:
type: changed
---
title: Remove Duplicate Dashboard item from dashboards dropdown
merge_request: 38053
author:
type: changed
---
title: Allow to create merge request pipelines in target project when user has permission
merge_request: 37322
author:
type: added
......@@ -12,6 +12,10 @@ class UnconfirmWrongfullyVerifiedEmails < ActiveRecord::Migration[6.0]
MIGRATION = 'WrongfullyConfirmedEmailUnconfirmer'
EMAIL_INDEX_NAME = 'tmp_index_for_email_unconfirmation_migration'
class ApplicationSetting < ActiveRecord::Base
self.table_name = 'application_settings'
end
class Email < ActiveRecord::Base
include EachBatch
end
......@@ -19,6 +23,11 @@ class UnconfirmWrongfullyVerifiedEmails < ActiveRecord::Migration[6.0]
def up
add_concurrent_index :emails, :id, where: 'confirmed_at IS NOT NULL', name: EMAIL_INDEX_NAME
ApplicationSetting.reset_column_information
setting_record = ApplicationSetting.last
return unless setting_record&.send_user_confirmation_email
queue_background_migration_jobs_by_range_at_intervals(Email,
MIGRATION,
INTERVAL,
......
......@@ -83,7 +83,7 @@ The resulting `.yml` file can be customized and adapted to your project.
You can decide to save the dashboard `.yml` file in the project's **default** branch or in a
new branch.
1. Click **Duplicate dashboard** in the dashboard dropdown or in the actions menu.
1. Click **Duplicate dashboard** in the actions menu.
NOTE: **Note:**
You can duplicate only GitLab-defined dashboards.
......
......@@ -46,8 +46,8 @@ The following table depicts the various user permission levels in a project.
| Action | Guest | Reporter | Developer |Maintainer| Owner* |
|---------------------------------------------------|---------|------------|-------------|----------|--------|
| Download project | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Leave comments | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View allowed and denied licenses **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Leave comments | ✓ | ✓ | ✓ | ✓ | ✓ |
| View allowed and denied licenses **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View License Compliance reports **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| View Security reports **(ULTIMATE)** | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
| View Dependency list **(ULTIMATE)** | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
......@@ -61,9 +61,9 @@ The following table depicts the various user permission levels in a project.
| See a list of jobs | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
| See a job log | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
| Download and browse job artifacts | ✓ (*3*) | ✓ | ✓ | ✓ | ✓ |
| Create new issue | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Create new issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| See related issues | ✓ | ✓ | ✓ | ✓ | ✓ |
| Create confidential issue | ✓ (*1*) | ✓ | ✓ | ✓ | ✓ |
| Create confidential issue | ✓ | ✓ | ✓ | ✓ | ✓ |
| View confidential issues | (*2*) | ✓ | ✓ | ✓ | ✓ |
| View [Releases](project/releases/index.md) | ✓ (*6*) | ✓ | ✓ | ✓ | ✓ |
| View requirements **(ULTIMATE)** | ✓ | ✓ | ✓ | ✓ | ✓ |
......
......@@ -30,6 +30,7 @@ module Gitlab
.where('emails.confirmed_at IS NOT NULL')
.where('emails.confirmed_at = users.confirmed_at')
.where('emails.email <> users.email')
.where('NOT EXISTS (SELECT 1 FROM user_synced_attributes_metadata WHERE user_id=users.id AND email_synced IS true)')
end
end
......
......@@ -71,7 +71,7 @@ module Gitlab
end
def self.allow_to_create_merge_request_pipelines_in_target_project?(target_project)
::Feature.enabled?(:ci_allow_to_create_merge_request_pipelines_in_target_project, target_project)
::Feature.enabled?(:ci_allow_to_create_merge_request_pipelines_in_target_project, target_project, default_enabled: true)
end
def self.ci_plan_needs_size_limit?(project)
......
......@@ -20,7 +20,6 @@ exports[`Dashboard template matches the default snapshot 1`] = `
data-qa-selector="dashboards_filter_dropdown"
defaultbranch="master"
id="monitor-dashboards-dropdown"
modalid="duplicateDashboard"
toggle-class="dropdown-menu-toggle"
/>
</div>
......@@ -134,11 +133,6 @@ exports[`Dashboard template matches the default snapshot 1`] = `
<!---->
</div>
<duplicate-dashboard-modal-stub
defaultbranch="master"
modalid="duplicateDashboard"
/>
</div>
<empty-state-stub
......
......@@ -208,14 +208,10 @@ describe('Dashboard header', () => {
describe('when a dashboard has been duplicated in the duplicate dashboard modal', () => {
beforeEach(() => {
store.state.monitoringDashboard.projectPath = 'root/sandbox';
setupAllDashboards(store, dashboardGitResponse[0].path);
});
/**
* The duplicate dashboard modal gets called both by a menu item from the
* dashboards dropdown and by an item from the actions menu.
*
* This spec is context agnostic, so it addresses all cases where the
* duplicate dashboard modal gets called.
*/
it('redirects to the newly created dashboard', () => {
delete window.location;
window.location = new URL('https://localhost');
......@@ -252,7 +248,7 @@ describe('Dashboard header', () => {
expect(findActionsMenu().exists()).toBe(false);
});
it('contains a modal', () => {
it('contains the create dashboard modal', () => {
store.state.monitoringDashboard.projectPath = mockProjectPath;
return wrapper.vm.$nextTick().then(() => {
......@@ -270,13 +266,14 @@ describe('Dashboard header', () => {
describe.each(duplicableCases)(
'when the selected dashboard can be duplicated',
dashboardPath => {
it('contains a "Create New" menu item and a "Duplicate Dashboard" menu item', () => {
it('contains menu items for "Create New", "Duplicate Dashboard" and a modal for duplicating dashboards', () => {
store.state.monitoringDashboard.projectPath = mockProjectPath;
setupAllDashboards(store, dashboardPath);
return wrapper.vm.$nextTick().then(() => {
expect(findCreateDashboardMenuItem().exists()).toBe(true);
expect(findCreateDashboardDuplicateItem().exists()).toBe(true);
expect(findDuplicateDashboardModal().exists()).toBe(true);
});
});
},
......@@ -290,13 +287,14 @@ describe('Dashboard header', () => {
describe.each(nonDuplicableCases)(
'when the selected dashboard cannot be duplicated',
dashboardPath => {
it('contains a "Create New" menu item and no "Duplicate Dashboard" menu item', () => {
it('contains a "Create New" menu item, but no "Duplicate Dashboard" menu item and modal', () => {
store.state.monitoringDashboard.projectPath = mockProjectPath;
setupAllDashboards(store, dashboardPath);
return wrapper.vm.$nextTick().then(() => {
expect(findCreateDashboardMenuItem().exists()).toBe(true);
expect(findCreateDashboardDuplicateItem().exists()).toBe(false);
expect(findDuplicateDashboardModal().exists()).toBe(false);
});
});
},
......
......@@ -3,10 +3,9 @@ import { GlDeprecatedDropdownItem, GlIcon } from '@gitlab/ui';
import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue';
import { dashboardGitResponse, selfMonitoringDashboardGitResponse } from '../mock_data';
import { dashboardGitResponse } from '../mock_data';
const defaultBranch = 'master';
const modalId = 'duplicateDashboardModalId';
const starredDashboards = dashboardGitResponse.filter(({ starred }) => starred);
const notStarredDashboards = dashboardGitResponse.filter(({ starred }) => !starred);
......@@ -17,9 +16,6 @@ describe('DashboardsDropdown', () => {
function createComponent(props, opts = {}) {
const storeOpts = {
methods: {
duplicateSystemDashboard: jest.fn(),
},
computed: {
allDashboards: () => mockDashboards,
selectedDashboard: () => mockSelectedDashboard,
......@@ -30,7 +26,6 @@ describe('DashboardsDropdown', () => {
propsData: {
...props,
defaultBranch,
modalId,
},
sync: false,
...storeOpts,
......@@ -150,87 +145,6 @@ describe('DashboardsDropdown', () => {
});
});
const duplicableCases = [
dashboardGitResponse[0],
dashboardGitResponse[2],
selfMonitoringDashboardGitResponse[0],
];
describe.each(duplicableCases)('when the selected dashboard can be duplicated', dashboard => {
let duplicateDashboardAction;
let modalDirective;
beforeEach(() => {
mockSelectedDashboard = dashboard;
modalDirective = jest.fn();
duplicateDashboardAction = jest.fn().mockResolvedValue();
wrapper = createComponent(
{},
{
directives: {
GlModal: modalDirective,
},
methods: {
// Mock vuex actions
duplicateSystemDashboard: duplicateDashboardAction,
},
},
);
});
it('displays a dropdown item for each dashboard', () => {
expect(findItems().length).toEqual(dashboardGitResponse.length + 1);
});
it('displays one "duplicate dashboard" dropdown item with a directive attached', () => {
const item = wrapper.findAll('[data-testid="duplicateDashboardItem"]');
expect(item.length).toBe(1);
});
it('"duplicate dashboard" dropdown item directive works', () => {
const item = wrapper.find('[data-testid="duplicateDashboardItem"]');
item.trigger('click');
return wrapper.vm.$nextTick().then(() => {
expect(modalDirective).toHaveBeenCalled();
});
});
it('id is correct, as the value of modal directive binding matches modal id', () => {
expect(modalDirective).toHaveBeenCalledTimes(1);
// Binding's second argument contains the modal id
expect(modalDirective.mock.calls[0][1]).toEqual(
expect.objectContaining({
value: modalId,
}),
);
});
});
const nonDuplicableCases = [dashboardGitResponse[1], selfMonitoringDashboardGitResponse[1]];
describe.each(nonDuplicableCases)(
'when the selected dashboard can not be duplicated',
dashboard => {
beforeEach(() => {
mockSelectedDashboard = dashboard;
wrapper = createComponent();
});
it('displays a dropdown list item for each dashboard, but no list item for "duplicate dashboard"', () => {
const item = wrapper.findAll('[data-testid="duplicateDashboardItem"]');
expect(findItems()).toHaveLength(dashboardGitResponse.length);
expect(item.length).toBe(0);
});
},
);
describe('when a dashboard gets selected by the user', () => {
beforeEach(() => {
wrapper = createComponent();
......
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlDeprecatedButton, GlLoadingIcon } from '@gitlab/ui';
import { GlButton, GlLoadingIcon } from '@gitlab/ui';
import DropdownTitle from '~/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue';
import labelsSelectModule from '~/vue_shared/components/sidebar/labels_select_vue/store';
......@@ -42,7 +42,7 @@ describe('DropdownTitle', () => {
});
it('renders edit link', () => {
const editBtnEl = wrapper.find(GlDeprecatedButton);
const editBtnEl = wrapper.find(GlButton);
expect(editBtnEl.exists()).toBe(true);
expect(editBtnEl.text()).toBe('Edit');
......
......@@ -5,6 +5,7 @@ require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::WrongfullyConfirmedEmailUnconfirmer, schema: 20200615111857 do
let(:users) { table(:users) }
let(:emails) { table(:emails) }
let(:user_synced_attributes_metadata) { table(:user_synced_attributes_metadata) }
let(:confirmed_at_2_days_ago) { 2.days.ago }
let(:confirmed_at_3_days_ago) { 3.days.ago }
let(:one_year_ago) { 1.year.ago }
......@@ -14,6 +15,8 @@ RSpec.describe Gitlab::BackgroundMigration::WrongfullyConfirmedEmailUnconfirmer,
let!(:user_does_not_need_migration) { users.create!(name: 'user3', email: 'test3@test.com', state: 'active', projects_limit: 1) }
let!(:inactive_user) { users.create!(name: 'user4', email: 'test4@test.com', state: 'blocked', projects_limit: 1, confirmed_at: confirmed_at_3_days_ago, confirmation_sent_at: one_year_ago) }
let!(:alert_bot_user) { users.create!(name: 'user5', email: 'test5@test.com', state: 'active', user_type: 2, projects_limit: 1, confirmed_at: confirmed_at_3_days_ago, confirmation_sent_at: one_year_ago) }
let!(:user_has_synced_email) { users.create!(name: 'user6', email: 'test6@test.com', state: 'active', projects_limit: 1, confirmed_at: confirmed_at_2_days_ago, confirmation_sent_at: one_year_ago) }
let!(:synced_attributes_metadata_for_user) { user_synced_attributes_metadata.create!(user_id: user_has_synced_email.id, email_synced: true) }
let!(:bad_email_1) { emails.create!(user_id: user_needs_migration_1.id, email: 'other1@test.com', confirmed_at: confirmed_at_2_days_ago, confirmation_sent_at: one_year_ago) }
let!(:bad_email_2) { emails.create!(user_id: user_needs_migration_2.id, email: 'other2@test.com', confirmed_at: confirmed_at_3_days_ago, confirmation_sent_at: one_year_ago) }
......@@ -24,8 +27,10 @@ RSpec.describe Gitlab::BackgroundMigration::WrongfullyConfirmedEmailUnconfirmer,
let!(:good_email_2) { emails.create!(user_id: user_needs_migration_2.id, email: 'other4@test.com', confirmed_at: nil) }
let!(:good_email_3) { emails.create!(user_id: user_does_not_need_migration.id, email: 'other5@test.com', confirmed_at: confirmed_at_2_days_ago, confirmation_sent_at: one_year_ago) }
let!(:second_email_for_user_with_synced_email) { emails.create!(user_id: user_has_synced_email.id, email: 'other6@test.com', confirmed_at: confirmed_at_2_days_ago, confirmation_sent_at: one_year_ago) }
subject do
email_ids = [bad_email_1, bad_email_2, good_email_1, good_email_2, good_email_3].map(&:id)
email_ids = [bad_email_1, bad_email_2, good_email_1, good_email_2, good_email_3, second_email_for_user_with_synced_email].map(&:id)
described_class.new.perform(email_ids.min, email_ids.max)
end
......@@ -61,10 +66,12 @@ RSpec.describe Gitlab::BackgroundMigration::WrongfullyConfirmedEmailUnconfirmer,
expect(user_does_not_need_migration.reload.confirmed_at).to be_nil
expect(inactive_user.reload.confirmed_at).to be_within(1.second).of(confirmed_at_3_days_ago)
expect(alert_bot_user.reload.confirmed_at).to be_within(1.second).of(confirmed_at_3_days_ago)
expect(user_has_synced_email.reload.confirmed_at).to be_within(1.second).of(confirmed_at_2_days_ago)
expect(user_does_not_need_migration.reload.confirmation_sent_at).to be_nil
expect(inactive_user.reload.confirmation_sent_at).to be_within(1.second).of(one_year_ago)
expect(alert_bot_user.reload.confirmation_sent_at).to be_within(1.second).of(one_year_ago)
expect(user_has_synced_email.confirmation_sent_at).to be_within(1.second).of(one_year_ago)
end
it 'updates confirmation_sent_at column' do
......
......@@ -9,13 +9,47 @@ RSpec.describe UnconfirmWrongfullyVerifiedEmails do
table(:emails).create!(email: 'test2@test.com', user_id: user.id)
end
it 'enqueues WrongullyConfirmedEmailUnconfirmer job' do
Sidekiq::Testing.fake! do
migrate!
context 'when email confirmation is enabled' do
before do
table(:application_settings).create!(send_user_confirmation_email: true)
end
it 'enqueues WrongullyConfirmedEmailUnconfirmer job' do
Sidekiq::Testing.fake! do
migrate!
jobs = BackgroundMigrationWorker.jobs
expect(jobs.size).to eq(1)
expect(jobs.first["args"].first).to eq(Gitlab::BackgroundMigration::WrongfullyConfirmedEmailUnconfirmer.name.demodulize)
end
end
end
context 'when email confirmation is disabled' do
before do
table(:application_settings).create!(send_user_confirmation_email: false)
end
it 'does not enqueue WrongullyConfirmedEmailUnconfirmer job' do
Sidekiq::Testing.fake! do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to eq(0)
end
end
end
context 'when email application setting record does not exist' do
before do
table(:application_settings).delete_all
end
it 'does not enqueue WrongullyConfirmedEmailUnconfirmer job' do
Sidekiq::Testing.fake! do
migrate!
jobs = BackgroundMigrationWorker.jobs
expect(jobs.size).to eq(1)
expect(jobs.first["args"].first).to eq(Gitlab::BackgroundMigration::WrongfullyConfirmedEmailUnconfirmer.name.demodulize)
expect(BackgroundMigrationWorker.jobs.size).to eq(0)
end
end
end
end
......@@ -61,6 +61,14 @@ RSpec.shared_examples 'wiki controller actions' do
expect(assigns(:sidebar_wiki_entries)).to be_nil
expect(assigns(:sidebar_limited)).to be_nil
end
context 'when the request is of non-html format' do
it 'returns a 404 error' do
get :pages, params: routing_params.merge(format: 'json')
expect(response).to have_gitlab_http_status(:not_found)
end
end
end
describe 'GET #history' do
......
......@@ -1157,7 +1157,7 @@
dom-accessibility-api "^0.4.5"
pretty-format "^25.5.0"
"@toast-ui/editor@^2.2.0", "@toast-ui/editor@^2.3.0":
"@toast-ui/editor@^2.3.0":
version "2.3.0"
resolved "https://registry.yarnpkg.com/@toast-ui/editor/-/editor-2.3.0.tgz#47a0bb4f7cec8248dda64cbbd2edf63294debcd8"
integrity sha512-rCb35CMxYS6U2aiwWhdLZMzbgzoVHm2YxGrlmH4OdNQNfzAM03DHl4lTwq7EP7E4MG0FEMgMyn0Ovo5DI7G8+w==
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册