Commit c1a50b81 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent b6847c62
...@@ -11,7 +11,6 @@ import { ...@@ -11,7 +11,6 @@ import {
FILE_VIEW_MODE_PREVIEW, FILE_VIEW_MODE_PREVIEW,
} from '../constants'; } from '../constants';
import Editor from '../lib/editor'; import Editor from '../lib/editor';
import ExternalLink from './external_link.vue';
import FileTemplatesBar from './file_templates/bar.vue'; import FileTemplatesBar from './file_templates/bar.vue';
import { __ } from '~/locale'; import { __ } from '~/locale';
...@@ -19,7 +18,6 @@ export default { ...@@ -19,7 +18,6 @@ export default {
components: { components: {
ContentViewer, ContentViewer,
DiffViewer, DiffViewer,
ExternalLink,
FileTemplatesBar, FileTemplatesBar,
}, },
props: { props: {
...@@ -275,8 +273,8 @@ export default { ...@@ -275,8 +273,8 @@ export default {
<template> <template>
<div id="ide" class="blob-viewer-container blob-editor-container"> <div id="ide" class="blob-viewer-container blob-editor-container">
<div class="ide-mode-tabs clearfix"> <div v-if="!shouldHideEditor && isEditModeActive" class="ide-mode-tabs clearfix">
<ul v-if="!shouldHideEditor && isEditModeActive" class="nav-links float-left border-bottom-0"> <ul class="nav-links float-left border-bottom-0">
<li :class="editTabCSS"> <li :class="editTabCSS">
<a <a
href="javascript:void(0);" href="javascript:void(0);"
...@@ -296,7 +294,6 @@ export default { ...@@ -296,7 +294,6 @@ export default {
> >
</li> </li>
</ul> </ul>
<external-link :file="file" />
</div> </div>
<file-templates-bar v-if="showFileTemplatesBar(file.name)" /> <file-templates-bar v-if="showFileTemplatesBar(file.name)" />
<div <div
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { GlIcon } from '@gitlab/ui'; import { GlIcon } from '@gitlab/ui';
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters } from 'vuex';
import { __ } from '~/locale'; import { __ } from '~/locale';
import Tracking from '~/tracking';
import { ASC, DESC } from '../constants'; import { ASC, DESC } from '../constants';
const SORT_OPTIONS = [ const SORT_OPTIONS = [
...@@ -14,6 +15,7 @@ export default { ...@@ -14,6 +15,7 @@ export default {
components: { components: {
GlIcon, GlIcon,
}, },
mixins: [Tracking.mixin()],
computed: { computed: {
...mapGetters(['sortDirection']), ...mapGetters(['sortDirection']),
selectedOption() { selectedOption() {
...@@ -31,6 +33,7 @@ export default { ...@@ -31,6 +33,7 @@ export default {
} }
this.setDiscussionSortDirection(direction); this.setDiscussionSortDirection(direction);
this.track('change_discussion_sort_direction', { property: direction });
}, },
isDropdownItemActive(sortDir) { isDropdownItemActive(sortDir) {
return sortDir === this.sortDirection; return sortDir === this.sortDirection;
......
...@@ -9,7 +9,13 @@ module Projects ...@@ -9,7 +9,13 @@ module Projects
def show def show
unless @project.import_state&.in_progress? unless @project.import_state&.in_progress?
jira_client = @project.jira_service.client jira_client = @project.jira_service.client
@jira_projects = jira_client.Project.all.map { |p| ["#{p.name} (#{p.key})", p.key] } jira_projects = jira_client.Project.all
if jira_projects.present?
@jira_projects = jira_projects.map { |p| ["#{p.name} (#{p.key})", p.key] }
else
flash[:alert] = 'No projects have been returned from Jira. Please check your Jira configuration.'
end
end end
flash[:notice] = _("Import %{status}") % { status: @project.import_state.status } if @project.import_state.present? && !@project.import_state.none? flash[:notice] = _("Import %{status}") % { status: @project.import_state.status } if @project.import_state.present? && !@project.import_state.none?
......
...@@ -1405,7 +1405,7 @@ class User < ApplicationRecord ...@@ -1405,7 +1405,7 @@ class User < ApplicationRecord
.select('ci_runners.*') .select('ci_runners.*')
group_runners = Ci::RunnerNamespace group_runners = Ci::RunnerNamespace
.where(namespace_id: owned_groups.select(:id)) .where(namespace_id: Gitlab::ObjectHierarchy.new(owned_groups).base_and_descendants.select(:id))
.joins(:runner) .joins(:runner)
.select('ci_runners.*') .select('ci_runners.*')
...@@ -1696,7 +1696,7 @@ class User < ApplicationRecord ...@@ -1696,7 +1696,7 @@ class User < ApplicationRecord
def gitlab_employee? def gitlab_employee?
strong_memoize(:gitlab_employee) do strong_memoize(:gitlab_employee) do
if Gitlab.com? if Feature.enabled?(:gitlab_employee_badge) && Gitlab.com?
Mail::Address.new(email).domain == "gitlab.com" && confirmed? Mail::Address.new(email).domain == "gitlab.com" && confirmed?
else else
false false
...@@ -1713,6 +1713,10 @@ class User < ApplicationRecord ...@@ -1713,6 +1713,10 @@ class User < ApplicationRecord
!confirmed? && !confirmation_period_valid? !confirmed? && !confirmation_period_valid?
end end
def organization
gitlab_employee? ? 'GitLab' : super
end
protected protected
# override, from Devise::Validatable # override, from Devise::Validatable
......
# frozen_string_literal: true # frozen_string_literal: true
class NoteUserEntity < UserEntity class NoteUserEntity < UserEntity
expose :gitlab_employee?, as: :is_gitlab_employee, if: ->(user, options) { ::Feature.enabled?(:gitlab_employee_badge) && user.gitlab_employee? } expose :gitlab_employee?, as: :is_gitlab_employee, if: ->(user, options) { user.gitlab_employee? }
unexpose :web_url unexpose :web_url
end end
...@@ -101,7 +101,7 @@ ...@@ -101,7 +101,7 @@
- else - else
= f.text_field :location, label: s_('Profiles|Location'), class: 'input-lg', placeholder: s_("Profiles|City, country") = f.text_field :location, label: s_('Profiles|Location'), class: 'input-lg', placeholder: s_("Profiles|City, country")
= f.text_field :job_title, class: 'input-md' = f.text_field :job_title, class: 'input-md'
= f.text_field :organization, label: s_('Profiles|Organization'), class: 'input-md', help: s_("Profiles|Who you represent or work for") = f.text_field :organization, readonly: @user.gitlab_employee?, label: s_('Profiles|Organization'), class: 'input-md', help: s_("Profiles|Who you represent or work for")
= f.text_area :bio, label: s_('Profiles|Bio'), rows: 4, maxlength: 250, help: s_("Profiles|Tell us about yourself in fewer than 250 characters") = f.text_area :bio, label: s_('Profiles|Bio'), rows: 4, maxlength: 250, help: s_("Profiles|Tell us about yourself in fewer than 250 characters")
%hr %hr
%h5= s_("Private profile") %h5= s_("Private profile")
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
%h3.page-title.d-flex.align-items-center %h3.page-title.d-flex.align-items-center
= sprite_icon('issues', size: 16, css_class: 'mr-1') = sprite_icon('issues', size: 16, css_class: 'mr-1')
= _('Import in progress') = _('Import in progress')
- else - elsif @jira_projects.present?
%h3.page-title.d-flex.align-items-center %h3.page-title.d-flex.align-items-center
= sprite_icon('issues', size: 16, css_class: 'mr-1') = sprite_icon('issues', size: 16, css_class: 'mr-1')
= _('Import issues from Jira') = _('Import issues from Jira')
......
---
title: Remove open in file view link from Web IDE
merge_request: 28705
author:
type: removed
---
title: 'Fix for issue 26426: Details of runners of nested groups of an owned group
are now available for users with enough permissions'
merge_request: 24169
author: nachootal@gmail.com
type: changed
...@@ -6567,6 +6567,9 @@ msgstr "" ...@@ -6567,6 +6567,9 @@ msgstr ""
msgid "Dependencies|All" msgid "Dependencies|All"
msgstr "" msgstr ""
msgid "Dependencies|Based on the %{linkStart}latest successful%{linkEnd} scan"
msgstr ""
msgid "Dependencies|Component" msgid "Dependencies|Component"
msgstr "" msgstr ""
...@@ -7124,9 +7127,6 @@ msgstr "" ...@@ -7124,9 +7127,6 @@ msgstr ""
msgid "Display source" msgid "Display source"
msgstr "" msgstr ""
msgid "Displays dependencies and known vulnerabilities, based on the %{linkStart}latest successful%{linkEnd} scan"
msgstr ""
msgid "Do not display offers from third parties within GitLab" msgid "Do not display offers from third parties within GitLab"
msgstr "" msgstr ""
......
...@@ -66,13 +66,29 @@ describe Projects::Import::JiraController do ...@@ -66,13 +66,29 @@ describe Projects::Import::JiraController do
context 'when running jira import first time' do context 'when running jira import first time' do
context 'get show' do context 'get show' do
it 'renders show template' do before do
allow(JIRA::Resource::Project).to receive(:all).and_return([]) allow(JIRA::Resource::Project).to receive(:all).and_return(jira_projects)
expect(project.import_state).to be_nil expect(project.import_state).to be_nil
get :show, params: { namespace_id: project.namespace.to_param, project_id: project } get :show, params: { namespace_id: project.namespace.to_param, project_id: project }
end
context 'when no projects have been retrieved from Jira' do
let(:jira_projects) { [] }
it 'render an error message' do
expect(flash[:alert]).to eq('No projects have been returned from Jira. Please check your Jira configuration.')
expect(response).to render_template(:show)
end
end
context 'when everything is ok' do
let(:jira_projects) { [double(name: 'FOO project', key: 'FOO')] }
expect(response).to render_template :show it 'renders show template' do
expect(response).to render_template(:show)
end
end end
end end
......
...@@ -3,6 +3,7 @@ import Vuex from 'vuex'; ...@@ -3,6 +3,7 @@ import Vuex from 'vuex';
import SortDiscussion from '~/notes/components/sort_discussion.vue'; import SortDiscussion from '~/notes/components/sort_discussion.vue';
import createStore from '~/notes/stores'; import createStore from '~/notes/stores';
import { ASC, DESC } from '~/notes/constants'; import { ASC, DESC } from '~/notes/constants';
import Tracking from '~/tracking';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -22,6 +23,7 @@ describe('Sort Discussion component', () => { ...@@ -22,6 +23,7 @@ describe('Sort Discussion component', () => {
beforeEach(() => { beforeEach(() => {
store = createStore(); store = createStore();
jest.spyOn(Tracking, 'event');
}); });
afterEach(() => { afterEach(() => {
...@@ -37,6 +39,9 @@ describe('Sort Discussion component', () => { ...@@ -37,6 +39,9 @@ describe('Sort Discussion component', () => {
wrapper.find('.js-newest-first').trigger('click'); wrapper.find('.js-newest-first').trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', DESC); expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', DESC);
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
property: DESC,
});
}); });
}); });
...@@ -58,6 +63,9 @@ describe('Sort Discussion component', () => { ...@@ -58,6 +63,9 @@ describe('Sort Discussion component', () => {
wrapper.find('.js-oldest-first').trigger('click'); wrapper.find('.js-oldest-first').trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', ASC); expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', ASC);
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
property: ASC,
});
}); });
it('applies the active class to the correct button in the dropdown', () => { it('applies the active class to the correct button in the dropdown', () => {
......
This diff is collapsed.
...@@ -6,20 +6,28 @@ describe API::Runners do ...@@ -6,20 +6,28 @@ describe API::Runners do
let(:admin) { create(:user, :admin) } let(:admin) { create(:user, :admin) }
let(:user) { create(:user) } let(:user) { create(:user) }
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:group_guest) { create(:user) }
let(:group_reporter) { create(:user) }
let(:group_developer) { create(:user) }
let(:group_maintainer) { create(:user) } let(:group_maintainer) { create(:user) }
let(:project) { create(:project, creator_id: user.id) } let(:project) { create(:project, creator_id: user.id) }
let(:project2) { create(:project, creator_id: user.id) } let(:project2) { create(:project, creator_id: user.id) }
let(:group) { create(:group).tap { |group| group.add_owner(user) } } let(:group) { create(:group).tap { |group| group.add_owner(user) } }
let(:subgroup) { create(:group, parent: group) }
let!(:shared_runner) { create(:ci_runner, :instance, description: 'Shared runner') } let!(:shared_runner) { create(:ci_runner, :instance, description: 'Shared runner') }
let!(:project_runner) { create(:ci_runner, :project, description: 'Project runner', projects: [project]) } let!(:project_runner) { create(:ci_runner, :project, description: 'Project runner', projects: [project]) }
let!(:two_projects_runner) { create(:ci_runner, :project, description: 'Two projects runner', projects: [project, project2]) } let!(:two_projects_runner) { create(:ci_runner, :project, description: 'Two projects runner', projects: [project, project2]) }
let!(:group_runner) { create(:ci_runner, :group, description: 'Group runner', groups: [group]) } let!(:group_runner_a) { create(:ci_runner, :group, description: 'Group runner A', groups: [group]) }
let!(:group_runner_b) { create(:ci_runner, :group, description: 'Group runner B', groups: [subgroup]) }
before do before do
# Set project access for users # Set project access for users
create(:group_member, :guest, user: group_guest, group: group)
create(:group_member, :reporter, user: group_reporter, group: group)
create(:group_member, :developer, user: group_developer, group: group)
create(:group_member, :maintainer, user: group_maintainer, group: group) create(:group_member, :maintainer, user: group_maintainer, group: group)
create(:project_member, :maintainer, user: user, project: project) create(:project_member, :maintainer, user: user, project: project)
create(:project_member, :maintainer, user: user, project: project2) create(:project_member, :maintainer, user: user, project: project2)
...@@ -41,7 +49,8 @@ describe API::Runners do ...@@ -41,7 +49,8 @@ describe API::Runners do
expect(json_response).to match_array [ expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'), a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner'), a_hash_including('description' => 'Two projects runner'),
a_hash_including('description' => 'Group runner') a_hash_including('description' => 'Group runner A'),
a_hash_including('description' => 'Group runner B')
] ]
end end
...@@ -131,7 +140,8 @@ describe API::Runners do ...@@ -131,7 +140,8 @@ describe API::Runners do
expect(json_response).to match_array [ expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'), a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner'), a_hash_including('description' => 'Two projects runner'),
a_hash_including('description' => 'Group runner'), a_hash_including('description' => 'Group runner A'),
a_hash_including('description' => 'Group runner B'),
a_hash_including('description' => 'Shared runner') a_hash_including('description' => 'Shared runner')
] ]
end end
...@@ -156,7 +166,8 @@ describe API::Runners do ...@@ -156,7 +166,8 @@ describe API::Runners do
expect(json_response).to match_array [ expect(json_response).to match_array [
a_hash_including('description' => 'Project runner'), a_hash_including('description' => 'Project runner'),
a_hash_including('description' => 'Two projects runner'), a_hash_including('description' => 'Two projects runner'),
a_hash_including('description' => 'Group runner') a_hash_including('description' => 'Group runner A'),
a_hash_including('description' => 'Group runner B')
] ]
end end
...@@ -165,7 +176,7 @@ describe API::Runners do ...@@ -165,7 +176,7 @@ describe API::Runners do
expect(response).to have_gitlab_http_status(:bad_request) expect(response).to have_gitlab_http_status(:bad_request)
end end
it 'filters runners by type' do it 'filters runners by project type' do
get api('/runners/all?type=project_type', admin) get api('/runners/all?type=project_type', admin)
expect(json_response).to match_array [ expect(json_response).to match_array [
...@@ -174,6 +185,15 @@ describe API::Runners do ...@@ -174,6 +185,15 @@ describe API::Runners do
] ]
end end
it 'filters runners by group type' do
get api('/runners/all?type=group_type', admin)
expect(json_response).to match_array [
a_hash_including('description' => 'Group runner A'),
a_hash_including('description' => 'Group runner B')
]
end
it 'does not filter by invalid type' do it 'does not filter by invalid type' do
get api('/runners/all?type=bogus', admin) get api('/runners/all?type=bogus', admin)
...@@ -526,15 +546,41 @@ describe API::Runners do ...@@ -526,15 +546,41 @@ describe API::Runners do
end.to change { Ci::Runner.project_type.count }.by(-1) end.to change { Ci::Runner.project_type.count }.by(-1)
end end
it 'does not delete group runner with guest access' do
delete api("/runners/#{group_runner_a.id}", group_guest)
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'does not delete group runner with reporter access' do
delete api("/runners/#{group_runner_a.id}", group_reporter)
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'does not delete group runner with developer access' do
delete api("/runners/#{group_runner_a.id}", group_developer)
expect(response).to have_gitlab_http_status(:forbidden)
end
it 'does not delete group runner with maintainer access' do it 'does not delete group runner with maintainer access' do
delete api("/runners/#{group_runner.id}", group_maintainer) delete api("/runners/#{group_runner_a.id}", group_maintainer)
expect(response).to have_gitlab_http_status(:forbidden) expect(response).to have_gitlab_http_status(:forbidden)
end end
it 'deletes group runner with owner access' do it 'deletes owned group runner with owner access' do
expect do
delete api("/runners/#{group_runner_a.id}", user)
expect(response).to have_gitlab_http_status(:no_content)
end.to change { Ci::Runner.group_type.count }.by(-1)
end
it 'deletes inherited group runner with owner access' do
expect do expect do
delete api("/runners/#{group_runner.id}", user) delete api("/runners/#{group_runner_b.id}", user)
expect(response).to have_gitlab_http_status(:no_content) expect(response).to have_gitlab_http_status(:no_content)
end.to change { Ci::Runner.group_type.count }.by(-1) end.to change { Ci::Runner.group_type.count }.by(-1)
...@@ -842,7 +888,7 @@ describe API::Runners do ...@@ -842,7 +888,7 @@ describe API::Runners do
get api("/groups/#{group.id}/runners", user) get api("/groups/#{group.id}/runners", user)
expect(json_response).to match_array([ expect(json_response).to match_array([
a_hash_including('description' => 'Group runner') a_hash_including('description' => 'Group runner A')
]) ])
end end
...@@ -851,7 +897,7 @@ describe API::Runners do ...@@ -851,7 +897,7 @@ describe API::Runners do
get api("/groups/#{group.id}/runners?type=group_type", user) get api("/groups/#{group.id}/runners?type=group_type", user)
expect(json_response).to match_array([ expect(json_response).to match_array([
a_hash_including('description' => 'Group runner') a_hash_including('description' => 'Group runner A')
]) ])
end end
...@@ -939,7 +985,7 @@ describe API::Runners do ...@@ -939,7 +985,7 @@ describe API::Runners do
end end
it 'does not enable group runner' do it 'does not enable group runner' do
post api("/projects/#{project.id}/runners", user), params: { runner_id: group_runner.id } post api("/projects/#{project.id}/runners", user), params: { runner_id: group_runner_a.id }
expect(response).to have_gitlab_http_status(:forbidden) expect(response).to have_gitlab_http_status(:forbidden)
end end
......
...@@ -19,4 +19,48 @@ describe 'profiles/show' do ...@@ -19,4 +19,48 @@ describe 'profiles/show' do
expect(rendered).to have_field('user_id', with: user.id) expect(rendered).to have_field('user_id', with: user.id)
end end
end end
context 'gitlab.com organization field' do
before do
allow(Gitlab).to receive(:com?).and_return(true)
end
context 'when `:gitlab_employee_badge` feature flag is enabled' do
context 'and when user has an `@gitlab.com` email address' do
let(:user) { create(:user, email: 'test@gitlab.com') }
it 'displays the organization field as `readonly` with a `value` of `GitLab`' do
render
expect(rendered).to have_selector('#user_organization[readonly][value="GitLab"]')
end
end
context 'and when a user does not have an `@gitlab.com` email' do
let(:user) { create(:user, email: 'test@example.com') }
it 'displays an editable organization field' do
render
expect(rendered).to have_selector('#user_organization:not([readonly]):not([value="GitLab"])')
end
end
end
context 'when `:gitlab_employee_badge` feature flag is disabled' do
before do
stub_feature_flags(gitlab_employee_badge: false)
end
context 'and when a user has an `@gitlab.com` email' do
let(:user) { create(:user, email: 'test@gitlab.com') }
it 'displays an editable organization field' do
render
expect(rendered).to have_selector('#user_organization:not([readonly]):not([value="GitLab"])')
end
end
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment