Commit 8c6b936b authored by Paul Slaughter's avatar Paul Slaughter Committed by Mark Chao

FE: Remove legacy approval feature in proj files

parent 2dd07926
import $ from 'jquery';
import _ from 'underscore';
import Api from 'ee/api';
import { __ } from '~/locale';
import Flash from '~/flash';
import axios from '~/lib/utils/axios_utils';
export default class ApproversSelect {
constructor() {
this.$approverSelect = $('.js-select-user-and-group');
const name = this.$approverSelect.data('name');
this.fieldNames = [`${name}[approver_ids]`, `${name}[approver_group_ids]`];
this.$loadWrapper = $('.load-wrapper');
this.bindEvents();
this.addEvents();
this.initSelect2();
}
bindEvents() {
this.handleSelectChange = this.handleSelectChange.bind(this);
this.fetchGroups = this.fetchGroups.bind(this);
this.fetchUsers = this.fetchUsers.bind(this);
}
addEvents() {
$(document).on('click', '.js-add-approvers', () => this.addApprover());
$(document).on('click', '.js-approver-remove', e => ApproversSelect.removeApprover(e));
}
static getApprovers(fieldName, approverList) {
const input = $(`[name="${fieldName}"]`);
const existingApprovers = $(approverList).map((i, el) => parseInt($(el).data('id'), 10));
const selectedApprovers = input
.val()
.split(',')
.filter(val => val !== '');
return [...existingApprovers, ...selectedApprovers];
}
fetchGroups(term) {
const options = {
skip_groups: ApproversSelect.getApprovers(this.fieldNames[1], '.js-approver-group'),
};
return Api.groups(term, options);
}
fetchUsers(term) {
const options = {
skip_users: ApproversSelect.getApprovers(this.fieldNames[0], '.js-approver'),
project_id: $('#project_id').val(),
};
return Api.approverUsers(term, options);
}
handleSelectChange(e) {
const { added, removed } = e;
const userInput = $(`[name="${this.fieldNames[0]}"]`);
const groupInput = $(`[name="${this.fieldNames[1]}"]`);
if (added) {
if (added.full_name) {
groupInput.val(`${groupInput.val()},${added.id}`.replace(/^,/, ''));
} else {
userInput.val(`${userInput.val()},${added.id}`.replace(/^,/, ''));
}
}
if (removed) {
if (removed.full_name) {
groupInput.val(groupInput.val().replace(new RegExp(`,?${removed.id}`), ''));
} else {
userInput.val(userInput.val().replace(new RegExp(`,?${removed.id}`), ''));
}
}
}
initSelect2() {
import(/* webpackChunkName: 'select2' */ 'select2/select2')
.then(() => {
this.$approverSelect
.select2({
placeholder: __('Search for users or groups'),
multiple: true,
minimumInputLength: 0,
query: query => {
const fetchGroups = this.fetchGroups(query.term);
const fetchUsers = this.fetchUsers(query.term);
return Promise.all([fetchGroups, fetchUsers]).then(([groups, users]) => {
const data = {
results: groups.concat(users),
};
return query.callback(data);
});
},
formatResult: ApproversSelect.formatResult,
formatSelection: ApproversSelect.formatSelection,
dropdownCss() {
const $input = $('.js-select-user-and-group .select2-input');
const offset = $input.offset();
let { left } = offset;
const inputRightPosition = left + $input.outerWidth();
const $dropdown = $('.select2-drop-active');
if ($dropdown.outerWidth() > $input.outerWidth()) {
left = `${inputRightPosition - $dropdown.width()}px`;
}
return {
left,
right: 'auto',
width: 'auto',
};
},
})
.on('change', this.handleSelectChange);
})
.catch(() => {});
}
static formatSelection(group) {
return _.escape(group.full_name || group.name);
}
static formatResult({
name,
username,
avatar_url: avatarUrl,
full_name: fullName,
full_path: fullPath,
}) {
if (username) {
const avatar = avatarUrl || gon.default_avatar_url;
return `
<div class="user-result">
<div class="user-image">
<img class="avatar s40" src="${avatar}">
</div>
<div class="user-info">
<div class="user-name">${_.escape(name)}</div>
<div class="user-username">@${_.escape(username)}</div>
</div>
</div>
`;
}
return `
<div class="group-result">
<div class="group-name">${_.escape(fullName)}</div>
<div class="group-path">${_.escape(fullPath)}</div>
</div>
`;
}
addApprover() {
this.fieldNames.forEach(ApproversSelect.saveApprovers);
}
static saveApprovers(fieldName) {
const $input = $(`[name="${fieldName}"]`);
const newValue = $input.val();
const $loadWrapper = $('.load-wrapper');
const $approverSelect = $('.js-select-user-and-group');
if (!newValue) {
return;
}
const $form = $('.js-add-approvers').closest('form');
$loadWrapper.removeClass('hidden');
axios
.post($form.attr('action'), `_method=PATCH&${[encodeURIComponent(fieldName)]}=${newValue}`, {
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
},
})
.then(({ data }) => {
ApproversSelect.updateApproverList(data);
ApproversSelect.saveApproversComplete($input, $approverSelect, $loadWrapper);
})
.catch(() => {
Flash(__('An error occurred while adding approver'));
ApproversSelect.saveApproversComplete($input, $approverSelect, $loadWrapper);
});
}
static saveApproversComplete($input, $approverSelect, $loadWrapper) {
$input.val('');
$approverSelect.select2('val', '').trigger('change');
$loadWrapper.addClass('hidden');
}
static removeApprover(e) {
e.preventDefault();
const target = e.currentTarget;
const $loadWrapper = $('.load-wrapper');
$loadWrapper.removeClass('hidden');
axios
.post(target.getAttribute('href'), '_method=DELETE', {
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
},
})
.then(({ data }) => {
ApproversSelect.updateApproverList(data);
$loadWrapper.addClass('hidden');
})
.catch(() => {
Flash(__('An error occurred while removing approver'));
$loadWrapper.addClass('hidden');
});
}
static updateApproverList(html) {
$('.js-current-approvers').html(
$(html)
.find('.js-current-approvers')
.html(),
);
}
}
......@@ -4,7 +4,6 @@ import '~/pages/projects/edit';
import UsersSelect from '~/users_select';
import UserCallout from '~/user_callout';
import groupsSelect from '~/groups_select';
import ApproversSelect from 'ee/approvers_select';
import mountApprovals from 'ee/approvals/mount_project_settings';
import initServiceDesk from 'ee/projects/settings_service_desk';
......@@ -14,7 +13,6 @@ document.addEventListener('DOMContentLoaded', () => {
new UserCallout({ className: 'js-service-desk-callout' });
new UserCallout({ className: 'js-mr-approval-callout' });
new ApproversSelect();
initServiceDesk();
mountApprovals(document.getElementById('js-mr-approvals-settings'));
});
import ApproversSelect from 'ee/approvers_select';
describe('ApproversSelect', () => {
describe('saveApproversComplete', () => {
let $input;
let $approverSelect;
let $loadWrapper;
beforeEach(() => {
$input = {
val: jasmine.createSpy(),
};
$approverSelect = {
select2: jasmine.createSpy().and.callFake(function() {
return this;
}),
trigger: jasmine.createSpy(),
};
$loadWrapper = {
addClass: jasmine.createSpy(),
};
ApproversSelect.saveApproversComplete($input, $approverSelect, $loadWrapper);
});
it('should empty the $input value', () => {
expect($input.val).toHaveBeenCalledWith('');
});
it('should empty the select2 value and trigger a change event', () => {
expect($approverSelect.select2).toHaveBeenCalledWith('val', '');
expect($approverSelect.trigger).toHaveBeenCalledWith('change');
});
it('should hide loadWrapper', () => {
expect($loadWrapper.addClass).toHaveBeenCalledWith('hidden');
});
});
describe('formatResult', () => {
it('escapes name', () => {
const output = ApproversSelect.formatResult({
name: '<script>alert("testing")</script>',
username: 'testing',
avatar_url: gl.TEST_HOST,
full_name: '<script>alert("testing")</script>',
full_path: 'testing',
});
expect(output).not.toContain('<script>alert("testing")</script>');
});
it('escapes full name', () => {
const output = ApproversSelect.formatResult({
username: 'testing',
avatar_url: gl.TEST_HOST,
full_name: '<script>alert("testing")</script>',
full_path: 'testing',
});
expect(output).not.toContain('<script>alert("testing")</script>');
});
});
describe('formatSelection', () => {
it('escapes full name', () => {
expect(
ApproversSelect.formatSelection({
full_name: '<script>alert("testing")</script>',
}),
).not.toBe('<script>alert("testing")</script>');
});
it('escapes name', () => {
expect(
ApproversSelect.formatSelection({
name: '<script>alert("testing")</script>',
}),
).not.toBe('<script>alert("testing")</script>');
});
});
});
......@@ -11306,9 +11306,6 @@ msgstr ""
msgid "Search for projects, issues, etc."
msgstr ""
msgid "Search for users or groups"
msgstr ""
msgid "Search forks"
msgstr ""
......
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