Page MenuHomeSoftware Heritage

D7374.diff
No OneTemporary

D7374.diff

diff --git a/assets/src/bundles/add_forge/add-request-history-item.ejs b/assets/src/bundles/add_forge/add-request-history-item.ejs
new file mode 100644
--- /dev/null
+++ b/assets/src/bundles/add_forge/add-request-history-item.ejs
@@ -0,0 +1,30 @@
+<%#
+ 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
+%>
+
+<div class="card">
+ <div class="card-header" id="heading<%= index %>}">
+ <h2 class="mb-0">
+ <button class="btn btn-link" type="button" data-toggle="collapse" data-target="#collapse<%= index %>"
+ aria-expanded="true" aria-controls="collapse$<%= index %>">
+ From <%= event.actor %> (<%= event.actor_role %>) on <%= event.date.slice(0, 16) %>
+ <%if (event.new_status !== null) { %>
+ <span style="padding-left: 10px;">New status:<span> <%= event.new_status %>
+ <% } %>
+ </button>
+ </h2>
+ </div>
+ <div id="collapse<%= index %>" class="collapse" aria-labelledby="headingOne" data-parent="#requestHistory">
+ <div class="card-body">
+ <p><%= event.text %></p>
+ <%if (event.new_status !== null) { %>
+ <p>
+ <span>Status changed to:<span> <strong><%= event.new_status %></strong>
+ </p>
+ <% } %>
+ </div>
+ </div>
+</div>
diff --git a/assets/src/bundles/add_forge/forge-admin-email.ejs b/assets/src/bundles/add_forge/forge-admin-email.ejs
new file mode 100644
--- /dev/null
+++ b/assets/src/bundles/add_forge/forge-admin-email.ejs
@@ -0,0 +1,28 @@
+<%#
+ 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
+%>
+Dear forge administrator,
+
+The mission of Software Heritage is to collect, preserve and share all the
+publicly available source code (see https://www.softwareheritage.org for more
+information).
+
+We just received a request to add the forge hosted at <%= forgeUrl %> to the
+list of software origins that are archived, and it is our understanding that you
+are the contact person for this forge.
+
+In order to archive the forge contents, we will have to periodically pull the
+public repositories it contains and clone them into the
+Software Heritage archive.
+
+Would you be so kind as to reply to this message to acknowledge the reception
+of this email and let us know if there are any special steps we should take in
+order to properly archive the public repositories hosted on your infrastructure?
+
+Thank you in advance for your help.
+
+Kind regards,
+The Software Heritage team
diff --git a/assets/src/bundles/add_forge/index.js b/assets/src/bundles/add_forge/index.js
--- a/assets/src/bundles/add_forge/index.js
+++ b/assets/src/bundles/add_forge/index.js
@@ -9,3 +9,4 @@
export * from './create-request';
export * from './moderation-dashboard';
+export * from './request-dashboard';
diff --git a/assets/src/bundles/add_forge/moderation-dashboard.js b/assets/src/bundles/add_forge/moderation-dashboard.js
--- a/assets/src/bundles/add_forge/moderation-dashboard.js
+++ b/assets/src/bundles/add_forge/moderation-dashboard.js
@@ -28,7 +28,10 @@
{
data: 'id',
name: 'id',
- render: $.fn.dataTable.render.text()
+ render: function(data, type, row, meta) {
+ const dashboardUrl = Urls.add_forge_now_request_dashboard(data);
+ return `<a href=${dashboardUrl}>${data}</a>`;
+ }
},
{
data: 'submission_date',
diff --git a/assets/src/bundles/add_forge/request-dashboard.js b/assets/src/bundles/add_forge/request-dashboard.js
new file mode 100644
--- /dev/null
+++ b/assets/src/bundles/add_forge/request-dashboard.js
@@ -0,0 +1,135 @@
+/**
+ * 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 {handleFetchError, csrfPost} from 'utils/functions';
+import emailTempate from './forge-admin-email.ejs';
+import requestHistoryItem from './add-request-history-item.ejs';
+
+export function onRequestDashboardLoad(requestId) {
+ $(document).ready(() => {
+ populateRequestDetails(requestId);
+
+ $('#contactForgeAdmin').click((event) => {
+ contactForgeAdmin(event);
+ });
+
+ $('#updateRequestForm').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);
+ $('#userMessage').text('The request status has been updated ');
+ $('#userMessage').removeClass('badge-danger');
+ $('#userMessage').addClass('badge-success');
+ populateRequestDetails(requestId);
+ } catch (response) {
+ $('#userMessage').text('Sorry; Updating the request failed');
+ $('#userMessage').removeClass('badge-success');
+ $('#userMessage').addClass('badge-danger');
+ }
+ });
+ });
+}
+
+async function populateRequestDetails(requestId) {
+ try {
+ const response = await fetch(Urls.api_1_add_forge_request_get(requestId));
+ handleFetchError(response);
+ const data = await response.json();
+ $('#requestStatus').text(data.request.status);
+ $('#requestType').text(data.request.forge_type);
+ $('#requestURL').text(data.request.forge_url);
+ $('#requestEmail').text(data.request.forge_contact_email);
+ $('#submitterMessage').text(data.request.forge_contact_comment);
+ $('#updateComment').val('');
+
+ // Setting data for the email, now adding static data
+ $('#swh-input-forge-admin-email').val(emailTempate({'forgeUrl': data.request.forge_url}).trim());
+ $('#contactForgeAdmin').attr('emailTo', data.request.forge_contact_email);
+ $('#contactForgeAdmin').attr('emailSubject', `[swh-add_forge_now] Request ${data.request.id}`);
+ populateRequestHistory(data.history);
+ populateDecisionSelectOption(data.request.status);
+ } catch (response) {
+ // The error message
+ $('#fetchError').removeClass('d-none');
+ $('#requestDetails').addClass('d-none');
+ }
+}
+
+function populateRequestHistory(history) {
+ $('#requestHistory').children().remove();
+
+ history.forEach((event, index) => {
+ const historyEvent = requestHistoryItem({'event': event, 'index': index});
+ $('#requestHistory').append(historyEvent);
+ });
+}
+
+export function populateDecisionSelectOption(currentStatus) {
+ const nextStatusesFor = {
+ 'PENDING': ['WAITING_FOR_FEEDBACK', 'REJECTED', 'SUSPENDED'],
+ 'WAITING_FOR_FEEDBACK': ['FEEDBACK_TO_HANDLE'],
+ 'FEEDBACK_TO_HANDLE': [
+ 'WAITING_FOR_FEEDBACK',
+ 'ACCEPTED',
+ 'REJECTED',
+ 'SUSPENDED'
+ ],
+ 'ACCEPTED': ['SCHEDULED'],
+ 'SCHEDULED': [
+ 'FIRST_LISTING_DONE',
+ 'FIRST_ORIGIN_LOADED'
+ ],
+ 'FIRST_LISTING_DONE': ['FIRST_ORIGIN_LOADED'],
+ 'FIRST_ORIGIN_LOADED': [],
+ 'REJECTED': [],
+ 'SUSPENDED': ['PENDING'],
+ 'DENIED': []
+ };
+
+ const statusLabel = {
+ 'PENDING': 'pending',
+ 'WAITING_FOR_FEEDBACK': 'waiting for feedback',
+ 'FEEDBACK_TO_HANDLE': 'feedback to handle',
+ 'ACCEPTED': 'accepted',
+ 'SCHEDULED': 'scheduled',
+ 'FIRST_LISTING_DONE': 'first listing done',
+ 'FIRST_ORIGIN_LOADED': 'first origin loaded',
+ 'REJECTED': 'rejected',
+ 'SUSPENDED': 'suspended',
+ 'DENIED': 'denied'
+ };
+
+ // Determine the possible next status out of the current one
+ const nextStatuses = nextStatusesFor[currentStatus];
+
+ function addStatusOption(status, index) {
+ // Push the next possible status options
+ const label = statusLabel[status];
+ $('#decisionOptions').append(
+ `<option value="${status}">${label}</option>`
+ );
+ }
+ // Remove all the options and add new ones
+ $('#decisionOptions').children().remove();
+ nextStatuses.forEach(addStatusOption);
+ $('#decisionOptions').append(
+ '<option hidden disabled selected value> -- Add a comment -- </option>'
+ );
+}
+
+function contactForgeAdmin(event) {
+ // Open the mailclient with pre-filled text
+ const mailTo = $('#contactForgeAdmin').attr('emailTo');
+ const subject = $('#contactForgeAdmin').attr('emailSubject');
+ const emailText = $('#swh-input-forge-admin-email').val().replace(/\n/g, '%0D%0A');
+ const w = window.open('', '_blank', '', true);
+ w.location.href = `mailto: ${mailTo}?subject=${subject}&body=${emailText}`;
+ w.focus();
+}
diff --git a/cypress/fixtures/add-forge-now-request.json b/cypress/fixtures/add-forge-now-request.json
new file mode 100644
--- /dev/null
+++ b/cypress/fixtures/add-forge-now-request.json
@@ -0,0 +1,23 @@
+{
+ "request":{
+ "id":1,
+ "status":"PENDING",
+ "submission_date":"2022-03-17T13:35:24.324848Z",
+ "submitter_name":"admin",
+ "submitter_email":"admin@swh-web.org",
+ "forge_type":"bitbucket",
+ "forge_url":"test.com",
+ "forge_contact_email":"test@example.com",
+ "forge_contact_name":"test user",
+ "forge_contact_comment":"test comment"
+ },"history":[
+ {
+ "id":1,
+ "text":"",
+ "actor":"admin",
+ "actor_role":"SUBMITTER",
+ "date":"2022-03-17T13:35:24.326190Z",
+ "new_status":"PENDING"
+ }
+ ]
+}
diff --git a/cypress/integration/add-forge-now-request-dashboard.spec.js b/cypress/integration/add-forge-now-request-dashboard.spec.js
new file mode 100644
--- /dev/null
+++ b/cypress/integration/add-forge-now-request-dashboard.spec.js
@@ -0,0 +1,68 @@
+/**
+ * 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
+ */
+
+const requestId = 1;
+
+describe('Test add forge now request dashboard load', function() {
+
+ beforeEach(function() {
+ const url = this.Urls.add_forge_now_request_dashboard(requestId);
+ cy.adminLogin();
+ cy.intercept(`${this.Urls.api_1_add_forge_request_get(requestId)}**`,
+ {fixture: 'add-forge-now-request'}).as('forgeAddRequest');
+ cy.visit(url);
+ });
+
+ it('should load add forge request details', function() {
+ cy.wait('@forgeAddRequest');
+ cy.get('#requestStatus')
+ .should('contain', 'PENDING');
+
+ cy.get('#requestType')
+ .should('contain', 'bitbucket');
+
+ cy.get('#requestURL')
+ .should('contain', 'test.com');
+
+ cy.get('#requestEmail')
+ .should('contain', 'test@example.com');
+ });
+
+ it('should not show any error message', function() {
+ cy.get('#fetchError')
+ .should('have.class', 'd-none');
+ cy.get('#requestDetails')
+ .should('not.have.class', 'd-none');
+ });
+
+ it('should show error message for an api error', function() {
+ const invalidRequestId = 2;
+ const url = this.Urls.add_forge_now_request_dashboard(invalidRequestId);
+ cy.visit(url);
+ cy.get('#fetchError')
+ .should('not.have.class', 'd-none');
+ cy.get('#requestDetails')
+ .should('have.class', 'd-none');
+ });
+
+ it('should load add forge request history', function() {
+ cy.get('#requestHistory')
+ .children()
+ .should('have.length', 1);
+
+ cy.get('#requestHistory')
+ .children()
+ .should('contain', 'New status: PENDING');
+ });
+
+ it('should load possible next status', function() {
+ // 3 possible next status and the comment option
+ cy.get('#decisionOptions')
+ .children()
+ .should('have.length', 4);
+ });
+});
diff --git a/swh/web/admin/add_forge_now.py b/swh/web/admin/add_forge_now.py
--- a/swh/web/admin/add_forge_now.py
+++ b/swh/web/admin/add_forge_now.py
@@ -28,3 +28,19 @@
"add_forge_now/requests-moderation.html",
{"heading": "Add forge now requests moderation"},
)
+
+
+@admin_route(
+ r"add-forge/request/(?P<request_id>(\d)+)/",
+ view_name="add-forge-now-request-dashboard",
+)
+@user_passes_test(_can_access_moderation, login_url=settings.LOGIN_URL)
+def add_forge_now_request_dashboard(request, request_id):
+ """Moderation dashboard to allow listing current requests.
+
+ """
+ return render(
+ request,
+ "add_forge_now/request-dashboard.html",
+ {"request_id": request_id, "heading": "Add forge now request dashboard"},
+ )
diff --git a/swh/web/templates/add_forge_now/request-dashboard.html b/swh/web/templates/add_forge_now/request-dashboard.html
new file mode 100644
--- /dev/null
+++ b/swh/web/templates/add_forge_now/request-dashboard.html
@@ -0,0 +1,135 @@
+{% 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' %}
+{% endblock %}
+
+{% block title %}{{heading}} &ndash; Software Heritage archive{% endblock %}
+
+{% block navbar-content %}
+<h4>Add forge now request dashboard</h4>
+
+{% endblock %}
+
+{% block content %}
+<div class="col-md-7 offset-md-2">
+ <div class="container">
+ <div id="fetchError" class="d-none">
+ <h3>Error fetching information about the request</h3>
+ </div>
+ <div id="requestDetails">
+ <div class="row">
+ <div class="col-md-12">
+ <label>Request Info</label>
+ <ul class="list-group mb-3">
+ <li class="list-group-item d-flex justify-content-between lh-condensed">
+ <div>
+ <h6 class="my-0">Status</h6>
+ </div>
+ <span class="text-muted" id="requestStatus"></span>
+ </li>
+ <li class="list-group-item d-flex justify-content-between lh-condensed">
+ <div>
+ <h6 class="my-0">Type</h6>
+ </div>
+ <span class="text-muted" id="requestType"></span>
+ </li>
+ <li class="list-group-item d-flex justify-content-between lh-condensed">
+ <div>
+ <h6 class="my-0">Forge URL</h6>
+ </div>
+ <span class="text-muted" id="requestURL"></span>
+ </li>
+ <li class="list-group-item d-flex justify-content-between lh-condensed">
+ <div>
+ <h6 class="my-0">Forge contact email</h6>
+ </div>
+ <span id="requestEmail"></span>
+ </li>
+ <li class="list-group-item d-flex justify-content-between lh-condensed"
+ title="Submitter comment">
+ <p id="submitterMessage"></p>
+ </li>
+ </ul>
+ </div>
+ </div>
+ <div class="row">
+ <div class="col-md-12">
+ <label>Request history</label>
+ <div class="accordion" id="requestHistory">
+ </div>
+ </div>
+ </div>
+
+ <div class="row">
+ <label style="padding-top: 10px;">Message for the Forge administrator (Editable)</label>
+ <div class="form-group col-md-12">
+ <textarea class="form-control" id="swh-input-forge-admin-email"
+ name="forge_contact_comment" rows="4"></textarea>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="form-group col-md-12">
+ <button class="btn btn-default float-right" id="contactForgeAdmin"
+ emailSubject="" emailTo="">
+ Send message to forge
+ </button>
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="form-group col-md-12">
+ <form method="POST"
+ action="{% url 'api-1-add-forge-request-update' request_id %}"
+ style="padding-top: 5px;" id="updateRequestForm">
+ {% csrf_token %}
+ <div class="form-row">
+ <div class="form-group col-md-6">
+ <label for="decisionOptions">Choose your decision</label>
+ <select class="form-control" id="decisionOptions" name="new_status">
+ </select>
+ </div>
+ </div>
+
+ <div class="form-row">
+ <div class="form-group col-md-12">
+ <label for="swh-input-forge-comment">Comment</label>
+ <textarea class="form-control" id="updateComment" name="text" rows="3" required></textarea>
+ <small class="form-text text-muted">
+ Enter any comment related to your decision.
+ </small>
+ </div>
+ </div>
+ <div class="form-group col-md-6">
+ <button type="submit" class="btn btn-default mb-2 btn-lg">Submit</button>
+ </div>
+
+ <div class="form-row">
+ <div class="col-md-12">
+ <h3 class="text-center">
+ <span id="userMessage" class="badge"></span>
+ </h3>
+ </div>
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<script>
+ swh.add_forge.onRequestDashboardLoad("{{ request_id }}");
+</script>
+{% endblock %}

File Metadata

Mime Type
text/plain
Expires
Wed, Dec 18, 3:54 PM (7 h, 59 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3216541

Event Timeline