Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F9343490
D7357.id26609.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Subscribers
None
D7357.id26609.diff
View Options
diff --git a/assets/src/bundles/add_forge/create-request.js b/assets/src/bundles/add_forge/create-request.js
new file mode 100644
--- /dev/null
+++ b/assets/src/bundles/add_forge/create-request.js
@@ -0,0 +1,91 @@
+/**
+ * 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, removeUrlFragment} from 'utils/functions';
+
+export function onCreateRequestPageLoad() {
+ if (window.location.hash === '#browse-requests') {
+ $('.nav-tabs a[href="#swh-add-forge-requests-list"]').tab('show');
+ }
+
+ $('#requestCreateForm').submit(async function(event) {
+ event.preventDefault();
+ try {
+ const response = await fetch($(this).attr('action'), {
+ method: $(this).attr('method'),
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded'
+ },
+ body: $(this).serialize()
+ });
+ handleFetchError(response);
+ $('#userMessageDetail').empty();
+ $('#userMessage').text('Your request has been submitted');
+ $('#userMessage').removeClass('badge-danger');
+ $('#userMessage').addClass('badge-success');
+ populateRequesBrowseList(); // Update the listing
+ } catch (response) {
+ const responseText = await response.json();
+ $('#userMessageDetail').empty();
+ $('#userMessage').text('Sorry; an error occurred');
+ $('#userMessageDetail').text(responseText.substring(0, 500));
+ $('#userMessage').removeClass('badge-success');
+ $('#userMessage').addClass('badge-danger');
+ }
+ });
+
+ $('#swh-add-forge-requests-list-tab').on('shown.bs.tab', () => {
+ window.location.hash = '#browse-requests';
+ });
+
+ $('#swh-add-forge-tab').on('shown.bs.tab', () => {
+ removeUrlFragment();
+ });
+
+ $('#swh-show-forge-add-requests-list').click(function(e) {
+ $('.nav-tabs a[href="#swh-add-forge-requests-list"]').tab('show');
+ });
+
+ populateRequesBrowseList(); // Load existing requests
+}
+
+export async function populateRequesBrowseList() {
+ $('#add-forge-request-browse')
+ .on('error.dt', (e, settings, techNote, message) => {
+ $('#add-forge-browse-request-error').text(message);
+ })
+ .DataTable({
+ serverSide: true,
+ processing: true,
+ retrieve: true,
+ searching: true,
+ info: false,
+ 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: 'submission_date',
+ name: 'submission_date'
+ },
+ {
+ data: 'forge_type',
+ name: 'forge_type'
+ },
+ {
+ data: 'forge_url',
+ name: 'forge_url'
+ },
+ {
+ data: 'status',
+ name: 'status'
+ }
+ ]
+ });
+}
diff --git a/assets/src/bundles/add_forge/index.js b/assets/src/bundles/add_forge/index.js
new file mode 100644
--- /dev/null
+++ b/assets/src/bundles/add_forge/index.js
@@ -0,0 +1,10 @@
+/**
+ * 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
+ */
+
+// bundle for add forge views
+
+export * from './create-request';
diff --git a/cypress/integration/add-forge-now-request-create.spec.js b/cypress/integration/add-forge-now-request-create.spec.js
new file mode 100644
--- /dev/null
+++ b/cypress/integration/add-forge-now-request-create.spec.js
@@ -0,0 +1,139 @@
+/**
+ * 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);
+ cy.get('#swh-input-forge-url').type(url);
+ cy.get('#swh-input-forge-contact-name').type(contact);
+ cy.get('#swh-input-forge-contact-email').type(email);
+ cy.get('#swh-input-forge-comment').type(comment);
+}
+
+describe('Test add-forge-request creation', function() {
+ beforeEach(function() {
+ this.addForgeNowUrl = this.Urls.forge_add();
+ });
+
+ it('should show both 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');
+ });
+
+ 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 here');
+ });
+
+ 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()}`);
+ });
+
+ 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-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');
+ });
+
+ 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.be.visible');
+ });
+
+ it('should update browse list on successful submission', function() {
+ cy.userLogin();
+ cy.visit(this.addForgeNowUrl);
+ populateForm('bitbucket', 'gitlab.com', 'test', 'test@example.com', 'on', 'test comment');
+ cy.get('#requestCreateForm').submit();
+
+ // Change tab
+ cy.get('#swh-add-forge-requests-list-tab').click();
+ 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', 'gitlab.com', 'test', 'test@example.com', 'on', 'test comment');
+ cy.get('#requestCreateForm').submit();
+
+ cy.get('#requestCreateForm').submit(); // Submitting the same data again
+
+ cy.get('#userMessage')
+ .should('have.class', 'badge-danger')
+ .should('contain', 'Sorry; an error occurred');
+ });
+});
diff --git a/swh/web/add_forge_now/views.py b/swh/web/add_forge_now/views.py
--- a/swh/web/add_forge_now/views.py
+++ b/swh/web/add_forge_now/views.py
@@ -3,22 +3,22 @@
# License: GNU Affero General Public License version 3, or any later version
# See top-level LICENSE file for more information
-from typing import Any, Dict
+from typing import Any, Dict, List
from django.conf.urls import url
from django.core.paginator import Paginator
from django.db.models import Q
from django.http.request import HttpRequest
from django.http.response import HttpResponse, JsonResponse
+from django.shortcuts import render
+from swh.web.add_forge_now.models import Request as AddForgeRequest
from swh.web.api.views.add_forge_now import (
AddForgeNowRequestPublicSerializer,
AddForgeNowRequestSerializer,
)
from swh.web.auth.utils import ADD_FORGE_MODERATOR_PERMISSION
-from .models import Request as AddForgeRequest
-
def add_forge_request_list_datatables(request: HttpRequest) -> HttpResponse:
"""Dedicated endpoint used by datatables to display the add-forge
@@ -78,10 +78,30 @@
return JsonResponse(table_data)
+FORGE_TYPES: List[str] = [
+ "bitbucket",
+ "cgit",
+ "gitlab",
+ "gitea",
+ "heptapod",
+]
+
+
+def create_request(request):
+ """View to create a new 'add_forge_now' request.
+
+ """
+
+ return render(
+ request, "add_forge_now/create-request.html", {"forge_types": FORGE_TYPES},
+ )
+
+
urlpatterns = [
url(
r"^add-forge/request/list/datatables$",
add_forge_request_list_datatables,
name="add-forge-request-list-datatables",
),
+ url(r"^add-forge/request/create/$", create_request, name="forge-add"),
]
diff --git a/swh/web/templates/add_forge_now/create-request.html b/swh/web/templates/add_forge_now/create-request.html
new file mode 100644
--- /dev/null
+++ b/swh/web/templates/add_forge_now/create-request.html
@@ -0,0 +1,234 @@
+{% 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 %}
+Add forge now – Software Heritage archive
+{% endblock %}
+
+{% block navbar-content %}
+<h4>Request the addition of a forge into the archive</h4>
+{% endblock %}
+
+{% block content %}
+<div class="col-md-12 offset-md-1">
+ <div class="col-md-8">
+ <h5 class="d-flex justify-content-between align-items-center mb-3">
+ </h5>
+ <p style="margin-top: 1rem;">
+ “Add forge now” provides a service for Software Heritage users to save a
+ complete forge in the Software Heritage archive by requesting the addition
+ of the forge URL into the list of regularly visited forges.
+ {% if not user.is_authenticated %}
+ <p>
+ You can submit an “Add forge now” request only when you are authenticated,
+ please login to submit the request.
+ </p>
+ {% endif %}
+ </p>
+ </div>
+
+ <!-- Tabs in the page -->
+ <div class="col-md-8">
+ <ul class="nav nav-tabs">
+ <li class="nav-item"><a class="nav-link active" data-toggle="tab" id="swh-add-forge-tab" href="#swh-add-forge-submit-request">Submit a Request</a></li>
+ <li class="nav-item"><a class="nav-link" data-toggle="tab" id="swh-add-forge-requests-list-tab" href="#swh-add-forge-requests-list">Browse Requests</a></li>
+ </ul>
+
+ <div class="tab-content">
+ <div id="swh-add-forge-submit-request" class="tab-pane active" style="padding-top: 10px;">
+ {% if not user.is_authenticated %}
+ <h3>
+ <p class="text-primary">
+ You must be logged in to submit an add forge request. Please
+ <a id="loginLink" href="{% url 'login' %}?next={% url 'forge-add' %}" class="link-primary">log in here</a>
+ </p>
+ </h3>
+ {% else %}
+
+ <form method="POST" action="{% url 'api-1-add-forge-request-create' %}"
+ id="requestCreateForm" class="collapse show">
+ {% csrf_token %}
+ <div class="form-row">
+ <div class="form-group col-md-5">
+ <label for="swh-input-forge-type">Forge type</label>
+ <select class="form-control" id="swh-input-forge-type" name="forge_type" autofocus>
+ {% for each in forge_types %}
+ <option value={{ each }}>{{ each}}</option>
+ {% endfor %}
+ </select>
+ <small class="form-text text-muted">
+ Supported forge types in software archive.
+ </small>
+ </div>
+
+ <div class="form-group col-md-7">
+ <label for="swh-input-forge-url">Forge URL</label>
+ <input type="text" class="form-control" id="swh-input-forge-url" name="forge_url" required>
+ <small class="form-text text-muted">
+ Remote URL of the forge to list.
+ </small>
+ </div>
+ </div>
+
+ <div class="form-row">
+ <div class="form-group col-md-5">
+ <label for="swh-input-forge-contact-name">Forge contact name</label>
+ <input type="text" class="form-control" name="forge_contact_name" id="swh-input-forge-contact-name" required>
+ <small class="form-text text-muted">
+ Name of the Forge administrator.
+ </small>
+ </div>
+
+ <div class="form-group col-md-7">
+ <label for="swh-input-forge-contact-email">Forge contact email</label>
+ <input type="email" class="form-control" name="forge_contact_email" id="swh-input-forge-contact-email" required>
+ <small class="form-text text-muted">
+ Email of the forge administrator. The given email address will not be used for any purpose outside the “add forge now” process.
+ </small>
+ </div>
+ </div>
+
+ <div class="form-row">
+ <div class="form-group form-check">
+ <input class="form-check-input" type="checkbox"
+ id="swh-input-consent-check" name="consent_to_add_name">
+ <label for="swh-input-consent-check">
+ I consent to add my username in the communication with the forge
+ </label>
+ </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="swh-input-forge-comment" name="forge_contact_comment" rows="3"></textarea>
+ <small class="form-text text-muted">
+ If you wish to leave a comment regarding your request.
+ </small>
+ </div>
+ </div>
+
+ <div class="form-row">
+ <div class="col-md-12">
+ <input id="swh-input-form-submit" type="submit" value="Submit Add Request" class="btn btn-default float-right">
+ </div>
+ </div>
+
+ <div class="form-row">
+ <div class="col-md-12">
+ <h3 class="text-center">
+ <span id="userMessage" class="badge"></span>
+ </h3>
+ <p class="text-center">
+ <span id="userMessageDetail"></span>
+ </p>
+ </div>
+ </div>
+ </form>
+ <div class="panel panel-info">
+ <div class="panel-heading">
+ <a data-toggle="collapse" href="#swh-forge-add-request-help" role="button" aria-expanded="false" class="text-primary">
+ For more information
+ </a>
+ </div>
+ <div id="swh-forge-add-request-help" class="collapse panel-body">
+ <p>
+ Once submitted, your "add forge" request can either be:
+ </p>
+ <ul>
+ <li>
+ <strong>Pending:</strong>
+ the request was submitted and is waiting for a moderator
+ to check its validity.
+ </li>
+
+ <li>
+ <strong>Waiting for feedback:</strong>
+ the request was processed by a moderator
+ and the forge was contacted, the request is waiting for feedback from the
+ forge.
+ </li>
+
+ <li>
+ <strong>Feedback to handle:</strong>
+ the forge has responded to the request and
+ there is feedback to handle for the request.</li>
+
+ <li>
+ <strong>Accepted:</strong>
+ the request has been accepted and waiting to be
+ scheduled.
+ </li>
+
+ <li>
+ <strong>Scheduled:</strong>
+ the request has been scheduled is considered
+ done.
+ </li>
+
+ <li>
+ <strong>First listing done:</strong>
+ The first listing of the forge is
+ completed.
+ </li>
+
+ <li>
+ <strong>First origin loaded:</strong>
+ The first origin or repository processed by
+ loader and archived (using a search query).
+ </li>
+
+ <li><strong>Rejected:</strong> the request is not a valid request and is rejected by a
+ Software Heritage moderator.</li>
+
+ <li><strong>Denied:</strong> the forge has requested not to archive the forge.</li>
+
+ <li><strong>Suspended:</strong> the request is for a forge with a non supported
+ VCS.</li>
+ </ul>
+ <p>
+ Once a add request has been submitted, you can follow its current status in
+ the <a id="swh-show-forge-add-requests-list" href="#">
+ submitted requests list
+ </a>. This process is depending on human interactions and might take a few days to be
+ handled (it primarily depends on the response time of the forge).
+ </p>
+ </div>
+ </div>
+ {% endif %}
+ </div>
+ <div id="swh-add-forge-requests-list" class="tab-pane fade show">
+ <table id="add-forge-request-browse" class="table swh-table swh-table-striped" style="width: 100%;">
+ <thead>
+ <tr>
+ <th>Submission date</th>
+ <th>Forge type</th>
+ <th>Forge URL</th>
+ <th>Status</th>
+ </tr>
+ </thead>
+ </table>
+ <div id="add-forge-browse-request-error"></div>
+ </div>
+ </div>
+ </div>
+</div>
+
+<script>
+ swh.add_forge.onCreateRequestPageLoad();
+</script>
+
+{% endblock %}
diff --git a/swh/web/templates/layout.html b/swh/web/templates/layout.html
--- a/swh/web/templates/layout.html
+++ b/swh/web/templates/layout.html
@@ -209,6 +209,12 @@
<p>Save code now</p>
</a>
</li>
+ <li class="nav-item swh-origin-save-item" title="Request adding a new forge listing">
+ <a href="{% url 'forge-add' %}" class="nav-link swh-forge-add-link">
+ <i style="color: #e20026;" class="nav-icon mdi mdi-24px mdi-anvil"></i>
+ <p>Add forge now</p>
+ </a>
+ </li>
<li class="nav-item swh-help-item" title="How to browse the archive ?">
<a href="#" class="nav-link swh-help-link" onclick="swh.guided_tour.guidedTourButtonClick(event)">
<i style="color: #e20026;" class="nav-icon mdi mdi-24px mdi-help-circle"></i>
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jul 3, 1:34 PM (6 d, 5 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3216754
Attached To
D7357: Create form for "Add forge now"
Event Timeline
Log In to Comment