diff --git a/cypress/e2e/add-forge-now-request-create.cy.js b/cypress/e2e/add-forge-now-request-create.cy.js
index ba72ccd8..eed84a42 100644
--- a/cypress/e2e/add-forge-now-request-create.cy.js
+++ b/cypress/e2e/add-forge-now-request-create.cy.js
@@ -1,293 +1,332 @@
/**
* Copyright (C) 2022 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
*/
function populateForm(type, url, contact, email, consent, comment) {
cy.get('#swh-input-forge-type').select(type);
if (url) {
cy.get('#swh-input-forge-url').clear().type(url);
}
cy.get('#swh-input-forge-contact-name').clear().type(contact);
cy.get('#swh-input-forge-contact-email').clear().type(email);
if (comment) {
cy.get('#swh-input-forge-comment').clear().type(comment);
}
cy.get('#swh-input-consent-check').click({force: consent === 'on'});
}
function submitForm() {
cy.get('#requestCreateForm input[type=submit]').click();
cy.get('#requestCreateForm').then($form => {
if ($form[0].checkValidity()) {
cy.wait('@addForgeRequestCreate');
}
});
}
function initTest(testEnv) {
testEnv.addForgeNowUrl = testEnv.Urls.forge_add_create();
testEnv.addForgeNowRequestCreateUrl = testEnv.Urls.api_1_add_forge_request_create();
testEnv.listAddForgeRequestsUrl = testEnv.Urls.add_forge_request_list_datatables();
cy.intercept('POST', testEnv.addForgeNowRequestCreateUrl + '**')
.as('addForgeRequestCreate');
cy.intercept(testEnv.listAddForgeRequestsUrl + '**')
.as('addForgeRequestsList');
}
describe('Browse requests list tests', function() {
beforeEach(function() {
initTest(this);
});
it('should not show user requests filter checkbox for anonymous users', function() {
cy.visit(this.addForgeNowUrl);
cy.get('#swh-add-forge-requests-list-tab').click();
cy.get('#swh-add-forge-user-filter').should('not.exist');
});
it('should show user requests filter checkbox for authenticated users', function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
cy.get('#swh-add-forge-requests-list-tab').click();
cy.get('#swh-add-forge-user-filter').should('exist').should('be.checked');
});
it('should only display user requests when filter is activated', function() {
// Clean up previous state
cy.task('db:add_forge_now:delete');
// 'user2' logs in and create requests
cy.user2Login();
cy.visit(this.addForgeNowUrl);
// create requests for the user 'user'
populateForm('gitlab', 'https://gitlab.org', 'admin', 'admin@example.org', 'on', '');
submitForm();
// user requests filter checkbox should be in the DOM
cy.get('#swh-add-forge-requests-list-tab').click();
cy.get('#swh-add-forge-user-filter').should('exist').should('be.checked');
// check unfiltered user requests
cy.get('tbody tr').then(rows => {
expect(rows.length).to.eq(1);
});
// user1 logout
cy.contains('a', 'logout').click();
// user logs in
cy.userLogin();
cy.visit(this.addForgeNowUrl);
populateForm('gitea', 'https://gitea.org', 'admin', 'admin@example.org', 'on', '');
submitForm();
populateForm('cgit', 'https://cgit.org', 'admin', 'admin@example.org', 'on', '');
submitForm();
// user requests filter checkbox should be in the DOM
cy.get('#swh-add-forge-requests-list-tab').click();
cy.get('#swh-add-forge-user-filter').should('exist').should('be.checked');
cy.wait('@addForgeRequestsList');
// check unfiltered user requests
cy.get('tbody tr').then(rows => {
expect(rows.length).to.eq(2);
});
cy.get('#swh-add-forge-user-filter')
.uncheck({force: true});
cy.wait('@addForgeRequestsList');
// Users now sees everything
cy.get('tbody tr').then(rows => {
expect(rows.length).to.eq(2 + 1);
});
});
+
+ it('should display search link when first forge origin has been loaded', function() {
+ const forgeUrl = 'https://cgit.example.org';
+ cy.intercept(this.listAddForgeRequestsUrl + '**', {body: {
+ 'recordsTotal': 1,
+ 'draw': 1,
+ 'recordsFiltered': 1,
+ 'data': [
+ {
+ 'id': 1,
+ 'inbound_email_address': 'add-forge-now+15.yPalKD34nGJ-FYHwKXdmPQVkQ2c@example.org',
+ 'status': 'FIRST_ORIGIN_LOADED',
+ 'submission_date': '2022-09-22T05:31:47.566000Z',
+ 'submitter_name': 'johndoe',
+ 'submitter_email': 'johndoe@example.org',
+ 'submitter_forward_username': true,
+ 'forge_type': 'cgit',
+ 'forge_url': forgeUrl,
+ 'forge_contact_email': 'admin@example.org',
+ 'forge_contact_name': 'Admin',
+ 'last_modified_date': '2022-09-22T05:31:47.576000Z'
+ }
+ ]
+ }}).as('addForgeRequestsList');
+
+ cy.visit(this.addForgeNowUrl);
+
+ cy.get('#swh-add-forge-requests-list-tab').click();
+
+ cy.wait('@addForgeRequestsList');
+
+ let originsSearchUrl = `${this.Urls.browse_search()}?q=${encodeURIComponent(forgeUrl)}`;
+ originsSearchUrl += '&with_visit=true&with_content=true';
+
+ cy.get('.swh-search-forge-origins')
+ .should('have.attr', 'href', originsSearchUrl);
+
+ });
+
});
describe('Test add-forge-request creation', function() {
beforeEach(function() {
initTest(this);
});
it('should show all the tabs for every user', function() {
cy.visit(this.addForgeNowUrl);
cy.get('#swh-add-forge-tab')
.should('have.class', 'nav-link');
cy.get('#swh-add-forge-requests-list-tab')
.should('have.class', 'nav-link');
cy.get('#swh-add-forge-requests-help-tab')
.should('have.class', 'nav-link');
});
it('should show create forge tab by default', function() {
cy.visit(this.addForgeNowUrl);
cy.get('#swh-add-forge-tab')
.should('have.class', 'active');
cy.get('#swh-add-forge-requests-list-tab')
.should('not.have.class', 'active');
});
it('should show login link for anonymous user', function() {
cy.visit(this.addForgeNowUrl);
cy.get('#loginLink')
.should('be.visible')
.should('contain', 'log in');
});
it('should bring back after login', function() {
cy.visit(this.addForgeNowUrl);
cy.get('#loginLink')
.should('have.attr', 'href')
.and('include', `${this.Urls.login()}?next=${this.Urls.forge_add_create()}`);
});
it('should change tabs on click', function() {
cy.visit(this.addForgeNowUrl);
cy.get('#swh-add-forge-requests-list-tab').click();
cy.get('#swh-add-forge-tab')
.should('not.have.class', 'active');
cy.get('#swh-add-forge-requests-list-tab')
.should('have.class', 'active');
cy.get('#swh-add-forge-requests-help-tab')
.should('not.have.class', 'active');
cy.url()
.should('include', `${this.Urls.forge_add_list()}`);
cy.get('.swh-add-forge-now-item')
.should('have.class', 'active');
cy.get('#swh-add-forge-requests-help-tab').click();
cy.get('#swh-add-forge-tab')
.should('not.have.class', 'active');
cy.get('#swh-add-forge-requests-list-tab')
.should('not.have.class', 'active');
cy.get('#swh-add-forge-requests-help-tab')
.should('have.class', 'active');
cy.url()
.should('include', `${this.Urls.forge_add_help()}`);
cy.get('.swh-add-forge-now-item')
.should('have.class', 'active');
cy.get('#swh-add-forge-tab').click();
cy.get('#swh-add-forge-tab')
.should('have.class', 'active');
cy.get('#swh-add-forge-requests-list-tab')
.should('not.have.class', 'active');
cy.get('#swh-add-forge-requests-help-tab')
.should('not.have.class', 'active');
cy.url()
.should('include', `${this.Urls.forge_add_create()}`);
cy.get('.swh-add-forge-now-item')
.should('have.class', 'active');
});
it('should show create form elements to authenticated user', function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
cy.get('#swh-input-forge-type')
.should('be.visible');
cy.get('#swh-input-forge-url')
.should('be.visible');
cy.get('#swh-input-forge-contact-name')
.should('be.visible');
cy.get('#swh-input-consent-check')
.should('be.visible');
cy.get('#swh-input-forge-comment')
.should('be.visible');
cy.get('#swh-input-form-submit')
.should('be.visible');
});
it('should show browse requests table for every user', function() {
// testing only for anonymous
cy.visit(this.addForgeNowUrl);
cy.get('#swh-add-forge-requests-list-tab').click();
cy.get('#add-forge-request-browse')
.should('be.visible');
cy.get('#loginLink')
.should('not.exist');
});
it('should update browse list on successful submission', function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
populateForm('bitbucket', 'https://gitlab.com', 'test', 'test@example.com', 'on', 'test comment');
submitForm();
cy.visit(this.addForgeNowUrl);
cy.get('#swh-add-forge-requests-list-tab').click();
cy.wait('@addForgeRequestsList');
cy.get('#add-forge-request-browse')
.should('be.visible')
.should('contain', 'gitlab.com');
cy.get('#add-forge-request-browse')
.should('be.visible')
.should('contain', 'Pending');
});
it('should show error message on conflict', function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
populateForm('bitbucket', 'https://gitlab.com', 'test', 'test@example.com', 'on', 'test comment');
submitForm();
submitForm(); // Submitting the same data again
cy.get('#userMessage')
.should('have.class', 'badge-danger')
.should('contain', 'already exists');
});
it('should show error message', function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
populateForm(
'bitbucket', '', 'test', 'test@example.com', 'off', 'comment'
);
submitForm();
cy.get('#requestCreateForm').then(
$form => expect($form[0].checkValidity()).to.be.false
);
});
it('should not validate form when forge URL is invalid', function() {
cy.userLogin();
cy.visit(this.addForgeNowUrl);
populateForm('bitbucket', 'bitbucket.org', 'test', 'test@example.com', 'on', 'test comment');
submitForm();
cy.get('#swh-input-forge-url')
.then(input => {
assert.isFalse(input[0].checkValidity());
});
});
});
diff --git a/cypress/e2e/add-forge-now-requests-moderation.cy.js b/cypress/e2e/add-forge-now-requests-moderation.cy.js
index c1ced073..99b55107 100644
--- a/cypress/e2e/add-forge-now-requests-moderation.cy.js
+++ b/cypress/e2e/add-forge-now-requests-moderation.cy.js
@@ -1,120 +1,161 @@
/**
* Copyright (C) 2022 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
*/
function logout() {
cy.contains('a', 'logout')
.click();
}
describe('Test "Add Forge Now" moderation Login/logout', function() {
beforeEach(function() {
this.addForgeModerationUrl = this.Urls.add_forge_now_requests_moderation();
});
it('should redirect to default page', function() {
cy.visit(this.addForgeModerationUrl)
.get('input[name="username"]')
.type('admin')
.get('input[name="password"]')
.type('admin')
.get('.container form button[type=submit]')
.click();
cy.location('pathname')
.should('be.equal', this.addForgeModerationUrl);
});
it('should redirect to correct page after login', function() {
cy.visit(this.addForgeModerationUrl)
.location('pathname')
.should('be.equal', this.Urls.login());
cy.adminLogin();
cy.visit(this.addForgeModerationUrl)
.location('pathname')
.should('be.equal', this.addForgeModerationUrl);
logout();
});
it('should not display moderation link in sidebar when anonymous', function() {
cy.visit(this.addForgeModerationUrl);
cy.get(`.sidebar a[href="${this.addForgeModerationUrl}"]`)
.should('not.exist');
});
it('should not display moderation link when connected as unprivileged user', function() {
cy.userLogin();
cy.visit(this.addForgeModerationUrl);
cy.get(`.sidebar a[href="${this.addForgeModerationUrl}"]`)
.should('not.exist');
});
it('should display moderation link in sidebar when connected as privileged user', function() {
cy.addForgeModeratorLogin();
cy.visit(this.addForgeModerationUrl);
cy.get(`.sidebar a[href="${this.addForgeModerationUrl}"]`)
.should('exist');
});
it('should display moderation link in sidebar when connected as staff member', function() {
cy.adminLogin();
cy.visit(this.addForgeModerationUrl);
cy.get(`.sidebar a[href="${this.addForgeModerationUrl}"]`)
.should('exist');
});
});
describe('Test "Add Forge Now" moderation listing', function() {
beforeEach(function() {
this.addForgeModerationUrl = this.Urls.add_forge_now_requests_moderation();
this.listAddForgeRequestsUrl = this.Urls.add_forge_request_list_datatables();
});
it('should list add-forge-now requests', function() {
cy.intercept(`${this.listAddForgeRequestsUrl}**`, {fixture: 'add-forge-now-requests'}).as('listRequests');
let expectedRequests;
cy.readFile('cypress/fixtures/add-forge-now-requests.json').then((result) => {
expectedRequests = result['data'];
});
cy.addForgeModeratorLogin();
cy.visit(this.addForgeModerationUrl);
cy.get('.swh-add-forge-now-moderation-item')
.should('have.class', 'active');
cy.wait('@listRequests').then((xhr) => {
cy.log('response:', xhr.response);
cy.log(xhr.response.body);
const requests = xhr.response.body.data;
cy.log('Requests: ', requests);
expect(requests.length).to.equal(expectedRequests.length);
cy.get('#swh-add-forge-now-moderation-list').find('tbody > tr').as('rows');
// only 2 entries
cy.get('@rows').each((row, idx, collection) => {
const request = requests[idx];
const expectedRequest = expectedRequests[idx];
assert.isNotNull(request);
assert.isNotNull(expectedRequest);
expect(request.id).to.be.equal(expectedRequest['id']);
expect(request.status).to.be.equal(expectedRequest['status']);
expect(request.submission_date).to.be.equal(expectedRequest['submission_date']);
expect(request.forge_type).to.be.equal(expectedRequest['forge_type']);
expect(request.forge_url).to.be.equal(expectedRequest['forge_url']);
});
});
});
+ it('should display useful links in requests table', function() {
+ const forgeUrl = 'https://cgit.example.org';
+ const requestId = 1;
+ cy.intercept(this.listAddForgeRequestsUrl + '**', {body: {
+ 'recordsTotal': 1,
+ 'draw': 1,
+ 'recordsFiltered': 1,
+ 'data': [
+ {
+ 'id': requestId,
+ 'inbound_email_address': 'add-forge-now+15.yPalKD34nGJ-FYHwKXdmPQVkQ2c@example.org',
+ 'status': 'FIRST_ORIGIN_LOADED',
+ 'submission_date': '2022-09-22T05:31:47.566000Z',
+ 'submitter_name': 'johndoe',
+ 'submitter_email': 'johndoe@example.org',
+ 'submitter_forward_username': true,
+ 'forge_type': 'cgit',
+ 'forge_url': forgeUrl,
+ 'forge_contact_email': 'admin@example.org',
+ 'forge_contact_name': 'Admin',
+ 'last_modified_date': '2022-09-22T05:31:47.576000Z',
+ 'last_moderator': 'foo@softwareheritage.org'
+ }
+ ]
+ }}).as('addForgeRequestsList');
+
+ cy.addForgeModeratorLogin();
+ cy.visit(this.addForgeModerationUrl);
+
+ cy.wait('@addForgeRequestsList');
+
+ let originsSearchUrl = `${this.Urls.browse_search()}?q=${encodeURIComponent(forgeUrl)}`;
+ originsSearchUrl += '&with_visit=true&with_content=true';
+
+ cy.get('.swh-forge-request-dashboard-link')
+ .should('have.attr', 'href', this.Urls.add_forge_now_request_dashboard(requestId));
+
+ cy.get('.swh-search-forge-origins')
+ .should('have.attr', 'href', originsSearchUrl);
+ });
+
});
diff --git a/swh/web/add_forge_now/assets/create-request.js b/swh/web/add_forge_now/assets/create-request.js
index b51503a2..8ea536af 100644
--- a/swh/web/add_forge_now/assets/create-request.js
+++ b/swh/web/add_forge_now/assets/create-request.js
@@ -1,134 +1,147 @@
/**
* Copyright (C) 2022 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 {swhSpinnerSrc} from 'utils/constants';
import {
csrfPost, errorMessageFromResponse, genLink, getHumanReadableDate,
handleFetchError, validateUrl
} from 'utils/functions';
import userRequestsFilterCheckboxFn from 'utils/requests-filter-checkbox.ejs';
let requestBrowseTable;
const addForgeCheckboxId = 'swh-add-forge-user-filter';
const userRequestsFilterCheckbox = userRequestsFilterCheckboxFn({
'inputId': addForgeCheckboxId,
'checked': true // by default, display only user requests
});
export function onCreateRequestPageLoad() {
$(document).ready(() => {
$('#requestCreateForm').submit(async function(event) {
event.preventDefault();
try {
const response = await csrfPost($(this).attr('action'),
{'Content-Type': 'application/x-www-form-urlencoded'},
$(this).serialize());
handleFetchError(response);
$('#userMessageDetail').empty();
$('#userMessage').text('Your request has been submitted');
$('#userMessage').removeClass('badge-danger');
$('#userMessage').addClass('badge-success');
requestBrowseTable.draw(); // redraw the table to update the list
} catch (errorResponse) {
$('#userMessageDetail').empty();
let errorMessage;
const errorData = await errorResponse.json();
// if (errorResponse.content_type === 'text/plain') { // does not work?
if (errorResponse.status === 409) {
errorMessage = errorData;
} else { // assuming json response
// const exception = errorData['exception'];
errorMessage = errorMessageFromResponse(
errorData, 'An unknown error occurred during the request creation');
}
$('#userMessage').text(errorMessage);
$('#userMessage').removeClass('badge-success');
$('#userMessage').addClass('badge-danger');
}
});
populateRequestBrowseList(); // Load existing requests
});
}
export function populateRequestBrowseList() {
requestBrowseTable = $('#add-forge-request-browse')
.on('error.dt', (e, settings, techNote, message) => {
$('#add-forge-browse-request-error').text(message);
})
.DataTable({
serverSide: true,
processing: true,
language: {
processing: `
`
},
retrieve: true,
searching: true,
// Layout configuration, see [1] for more details
// [1] https://datatables.net/reference/option/dom
dom: '<"row"<"col-sm-3"l><"col-sm-6 text-left user-requests-filter"><"col-sm-3"f>>' +
'<"row"<"col-sm-12"tr>>' +
'<"row"<"col-sm-5"i><"col-sm-7"p>>',
ajax: {
'url': Urls.add_forge_request_list_datatables(),
data: (d) => {
const checked = $(`#${addForgeCheckboxId}`).prop('checked');
// If this function is called while the page is loading, 'checked' is
// undefined. As the checkbox defaults to being checked, coerce this to true.
if (swh.webapp.isUserLoggedIn() && (checked === undefined || checked)) {
d.user_requests_only = '1';
}
}
},
fnInitComplete: function() {
if (swh.webapp.isUserLoggedIn()) {
$('div.user-requests-filter').html(userRequestsFilterCheckbox);
$(`#${addForgeCheckboxId}`).on('change', () => {
requestBrowseTable.draw();
});
}
},
columns: [
{
data: 'submission_date',
name: 'submission_date',
render: getHumanReadableDate
},
{
data: 'forge_type',
name: 'forge_type',
render: $.fn.dataTable.render.text()
},
{
data: 'forge_url',
name: 'forge_url',
render: (data, type, row) => {
const sanitizedURL = $.fn.dataTable.render.text().display(data);
return genLink(sanitizedURL, type, true);
}
},
{
data: 'status',
name: 'status',
render: function(data, type, row, meta) {
return swh.add_forge_now.formatRequestStatusName(data);
}
+ },
+ {
+ render: (data, type, row) => {
+ if (row.status === 'FIRST_ORIGIN_LOADED') {
+ const sanitizedURL = $.fn.dataTable.render.text().display(row.forge_url);
+ let originsSearchUrl = `${Urls.browse_search()}?q=${encodeURIComponent(sanitizedURL)}`;
+ originsSearchUrl += '&with_visit=true&with_content=true';
+ return `' +
+ '';
+ }
+ return '';
+ }
}
],
order: [[0, 'desc']]
});
}
export function validateForgeUrl(input) {
let customValidity = '';
if (!validateUrl(input.value.trim(), ['http:', 'https:'])) {
customValidity = 'The provided forge URL is not valid.';
}
input.setCustomValidity(customValidity);
}
diff --git a/swh/web/add_forge_now/assets/moderation-dashboard.js b/swh/web/add_forge_now/assets/moderation-dashboard.js
index ac22ec43..f84ff219 100644
--- a/swh/web/add_forge_now/assets/moderation-dashboard.js
+++ b/swh/web/add_forge_now/assets/moderation-dashboard.js
@@ -1,75 +1,93 @@
/**
* Copyright (C) 2022 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 {genLink, getHumanReadableDate} from 'utils/functions';
export function onModerationPageLoad() {
populateModerationList();
}
export async function populateModerationList() {
$('#swh-add-forge-now-moderation-list')
.on('error.dt', (e, settings, techNote, message) => {
$('#swh-add-forge-now-moderation-list-error').text(message);
})
.DataTable({
serverSide: true,
processing: true,
searching: true,
dom: '<<"d-flex justify-content-between align-items-center"f' +
'<"#list-exclude">l>rt<"bottom"ip>>',
ajax: {
'url': Urls.add_forge_request_list_datatables()
},
columns: [
{
data: 'id',
name: 'id',
render: function(data, type, row, meta) {
const dashboardUrl = Urls.add_forge_now_request_dashboard(data);
return `${data}`;
}
},
{
data: 'submission_date',
name: 'submission_date',
render: getHumanReadableDate
},
{
data: 'forge_type',
name: 'forge_type',
render: $.fn.dataTable.render.text()
},
{
data: 'forge_url',
name: 'forge_url',
render: (data, type, row) => {
const sanitizedURL = $.fn.dataTable.render.text().display(data);
return genLink(sanitizedURL, type, true);
}
},
{
data: 'last_moderator',
name: 'last_moderator',
render: $.fn.dataTable.render.text()
},
{
data: 'last_modified_date',
name: 'last_modified_date',
render: getHumanReadableDate
},
{
data: 'status',
name: 'status',
render: function(data, type, row, meta) {
return swh.add_forge_now.formatRequestStatusName(data);
}
+ },
+ {
+ render: (data, type, row) => {
+ let html = '
';
+ const dashboardUrl = Urls.add_forge_now_request_dashboard(row.id);
+ html += `
` +
+ '';
+ if (row.status === 'FIRST_ORIGIN_LOADED') {
+ const sanitizedURL = $.fn.dataTable.render.text().display(row.forge_url);
+ let originsSearchUrl = `${Urls.browse_search()}?q=${encodeURIComponent(sanitizedURL)}`;
+ originsSearchUrl += '&with_visit=true&with_content=true';
+ html += `
' +
+ '';
+ }
+ html += '
';
+ return html;
+ }
}
],
order: [[0, 'desc']]
});
}
diff --git a/swh/web/add_forge_now/templates/add-forge-list.html b/swh/web/add_forge_now/templates/add-forge-list.html
index 1987168f..d7d44450 100644
--- a/swh/web/add_forge_now/templates/add-forge-list.html
+++ b/swh/web/add_forge_now/templates/add-forge-list.html
@@ -1,24 +1,25 @@
{% extends "./add-forge-common.html" %}
{% comment %}
Copyright (C) 2022 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
{% endcomment %}
{% block tab_content %}
Submission date |
Forge type |
Forge URL |
Status |
+ |
{% endblock %}
diff --git a/swh/web/add_forge_now/templates/add-forge-requests-moderation.html b/swh/web/add_forge_now/templates/add-forge-requests-moderation.html
index 588f153a..6e1d3d3f 100644
--- a/swh/web/add_forge_now/templates/add-forge-requests-moderation.html
+++ b/swh/web/add_forge_now/templates/add-forge-requests-moderation.html
@@ -1,48 +1,49 @@
{% extends "layout.html" %}
{% comment %}
Copyright (C) 2022 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
{% endcomment %}
{% load render_bundle from webpack_loader %}
{% load static %}
{% block header %}
{% render_bundle 'add_forge_now' %}
{% endblock %}
{% block title %}{{ heading }} – Software Heritage archive{% endblock %}
{% block navbar-content %}
Add forge now moderation
{% endblock %}
{% block content %}
ID |
Submission date |
Forge type |
Forge URL |
Moderator Name |
Last Modified Date |
Status |
+ |
{% endblock %}