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 `${data}`; + } }, { 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,163 @@ +/** + * 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'; + +var emailTempate = `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 [URL] 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 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`; + +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) { + // const responseText = await response.json(); + $('#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 email, now adding static data + $('#swh-input-forge-admin-email').val(emailTempate); + $('#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 the error message + $('#fetchError').removeClass('d-none'); + $('#requestDetails').addClass('d-none'); + } +} + +function populateRequestHistory(history) { + $('#accordionExample').children().remove(); + + history.forEach((event, i) => { + let historyEvent = `
+
+

+ +

+
+
+
+

${event.text}

`; + if (event.new_status !== null) { + historyEvent += `

New status: ${event.new_status}

`; + } + historyEvent += '
'; + $('#accordionExample').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 option + const label = statusLabel[status]; + $('#decisionOptions').append( + `` + ); + } + // Remove all the options and add ones + $('#decisionOptions').children().remove(); + nextStatuses.forEach(addStatusOption); + $('#decisionOptions').append( + '' + ); +} + +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,86 @@ +/** + * 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 not let non moderator access page', function() { + // cy.userLogin(); + // cy.visit(this.Urls.add_forge_now_request_dashboard(requestId)); + // }); + + 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('#accordionExample') + .children() + .should('have.length', 1); + + cy.get('#accordionExample') + .children() + .should('contain', 'New status: PENDING'); + }); + + // it('should load right email template ', function() { + // }); + + // it('should open mailclient with right text', function() { + // // make sure the email address is right + // }); + + it('should load possible next status', function() { + // 3 possible status for a request in pending state + cy.get('#decisionOptions') + .children() + .should('have.length', 4); + }); + + it('should update the forge request', function() { + }); + + it('should update change the request details', function() { + }); +}); 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.+)/", + 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}} – Software Heritage archive{% endblock %} + +{% block navbar-content %} +

Add forge now request dashboard

+ +{% endblock %} + +{% block content %} +
+
+
+

Error fetching information about the request

+
+
+
+
+ +
    +
  • +
    +
    Status
    +
    + +
  • +
  • +
    +
    Type
    +
    + +
  • +
  • +
    +
    Forge URL
    +
    + +
  • +
  • +
    +
    Forge contact email
    +
    + +
  • +
  • +

    +
  • +
+
+
+
+
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+
+ {% csrf_token %} +
+
+ + +
+
+ +
+
+ + + + Enter any comment related to your decision. + +
+
+
+ +
+ +
+
+

+ +

+
+
+
+
+
+
+
+
+ + +{% endblock %}