diff --git a/swh/web/assets/src/bundles/admin/origin-save.js b/swh/web/assets/src/bundles/admin/origin-save.js
index 41d56cb2..e4878f0a 100644
--- a/swh/web/assets/src/bundles/admin/origin-save.js
+++ b/swh/web/assets/src/bundles/admin/origin-save.js
@@ -1,249 +1,252 @@
/**
* Copyright (C) 2018 The Software Heritage developers
* See the AUTHORS file at the top-level directory of this distribution
* License: GNU Affero General Public License version 3, or any later version
* See top-level LICENSE file for more information
*/
import {handleFetchError, csrfPost} from 'utils/functions';
let authorizedOriginTable;
let unauthorizedOriginTable;
let pendingSaveRequestsTable;
let acceptedSaveRequestsTable;
let rejectedSaveRequestsTable;
function enableRowSelection(tableSel) {
$(`${tableSel} tbody`).on('click', 'tr', function() {
if ($(this).hasClass('selected')) {
$(this).removeClass('selected');
} else {
$(`${tableSel} tr.selected`).removeClass('selected');
$(this).addClass('selected');
}
});
}
export function initOriginSaveAdmin() {
$(document).ready(() => {
+
+ $.fn.dataTable.ext.errMode = 'throw';
+
authorizedOriginTable = $('#swh-authorized-origin-urls').DataTable({
serverSide: true,
ajax: Urls.admin_origin_save_authorized_urls_list(),
columns: [{data: 'url', name: 'url'}],
scrollY: '50vh',
scrollCollapse: true,
info: false
});
enableRowSelection('#swh-authorized-origin-urls');
unauthorizedOriginTable = $('#swh-unauthorized-origin-urls').DataTable({
serverSide: true,
ajax: Urls.admin_origin_save_unauthorized_urls_list(),
columns: [{data: 'url', name: 'url'}],
scrollY: '50vh',
scrollCollapse: true,
info: false
});
enableRowSelection('#swh-unauthorized-origin-urls');
let columnsData = [
{
data: 'save_request_date',
name: 'request_date',
render: (data, type, row) => {
if (type === 'display') {
let date = new Date(data);
return date.toLocaleString();
}
return data;
}
},
{
data: 'origin_type',
name: 'origin_type'
},
{
data: 'origin_url',
name: 'origin_url',
render: (data, type, row) => {
if (type === 'display') {
return `${data}`;
}
return data;
}
}
];
pendingSaveRequestsTable = $('#swh-origin-save-pending-requests').DataTable({
serverSide: true,
ajax: Urls.browse_origin_save_requests_list('pending'),
columns: columnsData,
scrollY: '50vh',
scrollCollapse: true,
order: [[0, 'desc']]
});
enableRowSelection('#swh-origin-save-pending-requests');
rejectedSaveRequestsTable = $('#swh-origin-save-rejected-requests').DataTable({
serverSide: true,
ajax: Urls.browse_origin_save_requests_list('rejected'),
columns: columnsData,
scrollY: '50vh',
scrollCollapse: true,
order: [[0, 'desc']]
});
columnsData.push({
data: 'save_task_status',
name: 'save_task_status',
render: (data, type, row) => {
if (data === 'succeed') {
let browseOriginUrl = Urls.browse_origin(row.origin_url);
return `${data}`;
}
return data;
}
});
acceptedSaveRequestsTable = $('#swh-origin-save-accepted-requests').DataTable({
serverSide: true,
ajax: Urls.browse_origin_save_requests_list('accepted'),
columns: columnsData,
scrollY: '50vh',
scrollCollapse: true,
order: [[0, 'desc']]
});
$('#swh-origin-save-requests-nav-item').on('shown.bs.tab', () => {
pendingSaveRequestsTable.draw();
});
$('#swh-origin-save-url-filters-nav-item').on('shown.bs.tab', () => {
authorizedOriginTable.draw();
});
$('#swh-authorized-origins-tab').on('shown.bs.tab', () => {
authorizedOriginTable.draw();
});
$('#swh-unauthorized-origins-tab').on('shown.bs.tab', () => {
unauthorizedOriginTable.draw();
});
$('#swh-save-requests-pending-tab').on('shown.bs.tab', () => {
pendingSaveRequestsTable.draw();
});
$('#swh-save-requests-accepted-tab').on('shown.bs.tab', () => {
acceptedSaveRequestsTable.draw();
});
$('#swh-save-requests-rejected-tab').on('shown.bs.tab', () => {
rejectedSaveRequestsTable.draw();
});
$('#swh-save-requests-pending-tab').click(() => {
pendingSaveRequestsTable.ajax.reload(null, false);
});
$('#swh-save-requests-accepted-tab').click(() => {
acceptedSaveRequestsTable.ajax.reload(null, false);
});
$('#swh-save-requests-rejected-tab').click(() => {
rejectedSaveRequestsTable.ajax.reload(null, false);
});
});
}
export function addAuthorizedOriginUrl() {
let originUrl = $('#swh-authorized-url-prefix').val();
let addOriginUrl = Urls.admin_origin_save_add_authorized_url(originUrl);
csrfPost(addOriginUrl)
.then(handleFetchError)
.then(() => {
authorizedOriginTable.row.add({'url': originUrl}).draw();
})
.catch(response => {
swh.webapp.showModalMessage('Duplicated origin url prefix',
'The provided origin url prefix is already registered in the authorized list.');
});
}
export function removeAuthorizedOriginUrl() {
let originUrl = $('#swh-authorized-origin-urls tr.selected').text();
if (originUrl) {
let removeOriginUrl = Urls.admin_origin_save_remove_authorized_url(originUrl);
csrfPost(removeOriginUrl)
.then(handleFetchError)
.then(() => {
authorizedOriginTable.row('.selected').remove().draw();
})
.catch(() => {});
}
}
export function addUnauthorizedOriginUrl() {
let originUrl = $('#swh-unauthorized-url-prefix').val();
let addOriginUrl = Urls.admin_origin_save_add_unauthorized_url(originUrl);
csrfPost(addOriginUrl)
.then(handleFetchError)
.then(() => {
unauthorizedOriginTable.row.add({'url': originUrl}).draw();
})
.catch(() => {
swh.webapp.showModalMessage('Duplicated origin url prefix',
'The provided origin url prefix is already registered in the unauthorized list.');
});
}
export function removeUnauthorizedOriginUrl() {
let originUrl = $('#swh-unauthorized-origin-urls tr.selected').text();
if (originUrl) {
let removeOriginUrl = Urls.admin_origin_save_remove_unauthorized_url(originUrl);
csrfPost(removeOriginUrl)
.then(handleFetchError)
.then(() => {
unauthorizedOriginTable.row('.selected').remove().draw();
})
.catch(() => {});
}
}
export function acceptOriginSaveRequest() {
let selectedRow = pendingSaveRequestsTable.row('.selected');
if (selectedRow.length) {
let acceptOriginSaveRequestCallback = () => {
let rowData = selectedRow.data();
let acceptSaveRequestUrl = Urls.admin_origin_save_request_accept(rowData['origin_type'], rowData['origin_url']);
csrfPost(acceptSaveRequestUrl)
.then(() => {
pendingSaveRequestsTable.ajax.reload(null, false);
});
};
swh.webapp.showModalConfirm('Accept origin save request ?',
'Are you sure to accept this origin save request ?',
acceptOriginSaveRequestCallback);
}
}
export function rejectOriginSaveRequest() {
let selectedRow = pendingSaveRequestsTable.row('.selected');
if (selectedRow.length) {
let rejectOriginSaveRequestCallback = () => {
let rowData = selectedRow.data();
let rejectSaveRequestUrl = Urls.admin_origin_save_request_reject(rowData['origin_type'], rowData['origin_url']);
csrfPost(rejectSaveRequestUrl)
.then(() => {
pendingSaveRequestsTable.ajax.reload(null, false);
});
};
swh.webapp.showModalConfirm('Reject origin save request ?',
'Are you sure to reject this origin save request ?',
rejectOriginSaveRequestCallback);
}
}
diff --git a/swh/web/assets/src/bundles/browse/origin-save.js b/swh/web/assets/src/bundles/browse/origin-save.js
index 9252f7a8..e4b4da32 100644
--- a/swh/web/assets/src/bundles/browse/origin-save.js
+++ b/swh/web/assets/src/bundles/browse/origin-save.js
@@ -1,183 +1,185 @@
/**
* Copyright (C) 2018 The Software Heritage developers
* See the AUTHORS file at the top-level directory of this distribution
* License: GNU Affero General Public License version 3, or any later version
* See top-level LICENSE file for more information
*/
import {handleFetchError, csrfPost, isGitRepoUrl} from 'utils/functions';
import {validate} from 'validate.js';
let saveRequestsTable;
export function initOriginSave() {
$(document).ready(() => {
+ $.fn.dataTable.ext.errMode = 'throw';
+
fetch(Urls.browse_origin_save_types_list())
.then(response => response.json())
.then(data => {
for (let originType of data) {
$('#swh-input-origin-type').append(``);
}
});
saveRequestsTable = $('#swh-origin-save-requests').DataTable({
serverSide: true,
ajax: Urls.browse_origin_save_requests_list('all'),
columns: [
{
data: 'save_request_date',
name: 'request_date',
render: (data, type, row) => {
if (type === 'display') {
let date = new Date(data);
return date.toLocaleString();
}
return data;
}
},
{
data: 'origin_type',
name: 'origin_type'
},
{
data: 'origin_url',
name: 'origin_url',
render: (data, type, row) => {
if (type === 'display') {
return `${data}`;
}
return data;
}
},
{
data: 'save_request_status',
name: 'status'
},
{
data: 'save_task_status',
name: 'save_task_status',
render: (data, type, row) => {
if (data === 'succeed') {
let browseOriginUrl = Urls.browse_origin(row.origin_url);
if (row.visit_date) {
browseOriginUrl += `visit/${row.visit_date}/`;
}
return `${data}`;
}
return data;
}
}
],
scrollY: '50vh',
scrollCollapse: true,
order: [[0, 'desc']]
});
$('#swh-origin-save-requests-list-tab').on('shown.bs.tab', () => {
saveRequestsTable.draw();
});
$('#swh-save-origin-form').submit(event => {
event.preventDefault();
event.stopPropagation();
if (event.target.checkValidity()) {
$(event.target).removeClass('was-validated');
let originType = $('#swh-input-origin-type').val();
let originUrl = $('#swh-input-origin-url').val();
let addSaveOriginRequestUrl = Urls.browse_origin_save_request(originType, originUrl);
let grecaptchaData = {'g-recaptcha-response': grecaptcha.getResponse()};
let headers = {
'Accept': 'application/json',
'Content-Type': 'application/json'
};
let body = JSON.stringify(grecaptchaData);
csrfPost(addSaveOriginRequestUrl, headers, body)
.then(handleFetchError)
.then(response => response.json())
.then(data => {
if (data.save_request_status === 'accepted') {
$('#swh-origin-save-request-status').css('color', 'green');
$('#swh-origin-save-request-status').text(
'The "save code now" request has been accepted and will be processed as soon as possible.');
} else {
$('#swh-origin-save-request-status').css('color', '#fecd1b');
$('#swh-origin-save-request-status').text(
'The "save code now" request has been put in pending state and may be accepted for processing after manual review.');
}
grecaptcha.reset();
})
.catch(response => {
if (response.status === 403) {
$('#swh-origin-save-request-status').css('color', 'red');
$('#swh-origin-save-request-status').text(
'The "save code now" request has been rejected because the reCAPTCHA could not be validated or the provided origin url is blacklisted.');
}
grecaptcha.reset();
});
} else {
$(event.target).addClass('was-validated');
}
});
$('#swh-show-origin-save-requests-list').on('click', (event) => {
event.preventDefault();
$('.nav-tabs a[href="#swh-origin-save-requests-list"]').tab('show');
});
$('#swh-input-origin-url').on('input', function(event) {
let originUrl = $(this).val().trim();
$(this).val(originUrl);
$('#swh-input-origin-type option').each(function() {
let val = $(this).val();
if (val && originUrl.includes(val)) {
$(this).prop('selected', true);
}
});
});
});
}
export function validateSaveOriginUrl(input) {
let validUrl = validate({website: input.value}, {
website: {
url: {
schemes: ['http', 'https', 'svn', 'git']
}
}
}) === undefined;
let originType = $('#swh-input-origin-type').val();
if (originType === 'git' && validUrl) {
// additional checks for well known code hosting providers
let githubIdx = input.value.indexOf('://github.com');
let gitlabIdx = input.value.indexOf('://gitlab.');
let gitSfIdx = input.value.indexOf('://git.code.sf.net');
let bitbucketIdx = input.value.indexOf('://bitbucket.org');
if (githubIdx !== -1 && githubIdx <= 5) {
validUrl = isGitRepoUrl(input.value, 'github.com');
} else if (gitlabIdx !== -1 && gitlabIdx <= 5) {
let startIdx = gitlabIdx + 3;
let idx = input.value.indexOf('/', startIdx);
if (idx !== -1) {
let gitlabDomain = input.value.substr(startIdx, idx - startIdx);
// GitLab repo url needs to be suffixed by '.git' in order to be successfully loaded
validUrl = isGitRepoUrl(input.value, gitlabDomain) && input.value.endsWith('.git');
} else {
validUrl = false;
}
} else if (gitSfIdx !== -1 && gitSfIdx <= 5) {
validUrl = isGitRepoUrl(input.value, 'git.code.sf.net/p');
} else if (bitbucketIdx !== -1 && bitbucketIdx <= 5) {
validUrl = isGitRepoUrl(input.value, 'bitbucket.org');
}
}
if (validUrl) {
input.setCustomValidity('');
} else {
input.setCustomValidity('The origin url is not valid or does not reference a code repository');
}
}