+
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ Release notes
- Fix the validity of SPDX outputs.
https://github.com/aboutcode-org/dejacode/issues/180

- Add ability to start and delete package scans from the Product inventory tab.
https://github.com/aboutcode-org/dejacode/pull/281

### Version 5.2.0

- Add visual indicator in hierarchy views, when an object on the far left or far right
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ <h5 class="modal-title">Delete {% trans 'Scan' %}</h5>
<p><strong>Are you sure you want to delete this Scan?</strong></p>
<p>
All data and results related to the Scan will be deleted.<br>
If the Scan is in progress, it will be cancel.
If the Scan is in progress, it will be canceled.
</p>
</div>
<div class="modal-footer">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,38 @@ <h5 class="modal-title">Scan Package</h5>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<a id="submit-scan-request" href="{% url 'component_catalog:package_scan' user.dataspace object.uuid %}" class="btn btn-success">Submit Scan Request</a>
{% if is_xhr %}
<button hx-swap="innerHTML"
hx-on--before-request="
let target = document.querySelector(this.getAttribute('hx-target'));
if (target) {
const spinner = document.createElement('div');
spinner.classList.add('spinner-border', 'spinner-border-md');
// Create the span for accessibility
const span = document.createElement('span');
span.classList.add('visually-hidden');
span.textContent = 'Submitting Scan Request...';
// Append the span to the spinner div
spinner.appendChild(span);
// Replace the inner content of the target with the spinner
target.innerHTML = ''; // Clear any existing content
target.appendChild(spinner);
}
"
hx-on--error="
let target = document.querySelector(this.getAttribute('hx-target'));
if (target) target.innerHTML = 'Error!';
"
class="btn btn-success scan-confirm"
data-bs-dismiss="modal"
>
Submit Scan Request
</button>
{% else %}
<a id="submit-scan-request" class="btn btn-success" href="{% url 'component_catalog:package_scan' user.dataspace object.uuid %}">
Submit Scan Request
</a>
{% endif %}
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
<strong class="ms-1">{{ scan.runs.0.status|title }}</strong>
{% include 'component_catalog/includes/scan_status.html' with status=scan.runs.0.status only %}

{% if compact_mode %}
<div class="mt-1">
{% if view_url %}
<a class="text-dark" href="{{ view_url }}#scan" target="_blank" data-bs-toggle="tooltip" data-bs-title="View Scan results"><i class="far fa-file-alt"></i></a>&nbsp;
{% endif %}
{% if scan.download_result_url %}
<a class="text-dark" href="{{ scan.download_result_url }}" target="_blank" data-bs-toggle="tooltip" data-bs-title="Download Scan results"><i class="fas fa-download"></i></a>
{% endif %}
</div>
{% else %}
<div class="text-center">
{% if scan.download_result_url %}
<a class="btn btn-outline-dark btn-sm mt-2" href="{{ scan.download_result_url }}" target="_blank"><i class="fas fa-download"></i> Results</a>
{% endif %}
{% if scan.delete_url %}
<a class="btn btn-outline-danger btn-sm mt-2 scan_delete_link" href="#scan-delete-modal" role="button" data-delete-url="{{ scan.delete_url }}" title="Delete" data-bs-toggle="modal"><i class="far fa-trash-alt"></i></a>
{% endif %}
</div>
{% endif %}
<div class="text-center">
{% if scan.download_result_url %}
<a class="btn btn-outline-dark btn-sm mt-2" href="{{ scan.download_result_url }}" target="_blank"><i class="fas fa-download"></i> Results</a>
{% endif %}
{% if scan.delete_url %}
<a class="btn btn-outline-danger btn-sm mt-2 scan_delete_link" href="#scan-delete-modal" role="button" data-delete-url="{{ scan.delete_url }}" title="Delete" data-bs-toggle="modal"><i class="far fa-trash-alt"></i></a>
{% endif %}
</div>
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<div class="progress" style="width: 100%; margin-bottom: 0; height: .5rem;">
{% if status == 'success' %}
<div class="progress-bar {% if has_errors %}bg-warning{% else %}bg-success{% endif %}" role="progressbar" style="width: 100%" aria-label="Scan progress" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
<div class="progress-bar {% if has_errors %}bg-warning{% else %}bg-success{% endif %}" title="{{ status|title }}" role="progressbar" style="width: 100%" aria-label="Scan progress" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
{% elif status == 'failure' or status == "stopped" or status == "stale" %}
<div class="progress-bar bg-danger" role="progressbar" style="width: 100%" aria-label="Scan progress" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
<div class="progress-bar bg-danger" title="{{ status|title }}" role="progressbar" style="width: 100%" aria-label="Scan progress" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
{% elif status == 'warning' %}
<div class="progress-bar bg-warning" role="progressbar" style="width: 100%" aria-label="Scan progress" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
<div class="progress-bar bg-warning" title="{{ status|title }}" role="progressbar" style="width: 100%" aria-label="Scan progress" aria-valuenow="100" aria-valuemin="0" aria-valuemax="100"></div>
{% elif status == 'running' %}
<div class="progress-bar" role="progressbar" style="width: 40%" aria-label="Scan progress" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div>
<div class="progress-bar" title="{{ status|title }}" role="progressbar" style="width: 40%" aria-label="Scan progress" aria-valuenow="40" aria-valuemin="0" aria-valuemax="100"></div>
{% elif status == 'not_started' or status == 'queued' %}
<div class="progress-bar" role="progressbar" style="width: 0" aria-label="Scan progress" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
<div class="progress-bar" title="{{ status|title }}" role="progressbar" style="width: 0" aria-label="Scan progress" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"></div>
{% endif %}
</div>
8 changes: 5 additions & 3 deletions component_catalog/templates/component_catalog/scan_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@
{{ block.super }}
<script>
document.addEventListener('DOMContentLoaded', function () {
$('.scan_delete_link').on('click', function() {
let delete_url = $(this).data('delete-url');
$('#scan-delete-modal a.delete-confirm').attr('href', delete_url);
document.querySelectorAll('.scan_delete_link').forEach(link => {
link.addEventListener('click', function() {
let deleteUrl = this.getAttribute('data-delete-url');
document.querySelector('#scan-delete-modal a.delete-confirm').setAttribute('href', deleteUrl);
});
});
});
</script>
Expand Down
6 changes: 6 additions & 0 deletions component_catalog/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from component_catalog.views import ScanListView
from component_catalog.views import component_create_ajax_view
from component_catalog.views import delete_scan_view
from component_catalog.views import get_scan_progress_htmx_view
from component_catalog.views import package_create_ajax_view
from component_catalog.views import package_scan_view
from component_catalog.views import send_scan_data_as_file_view
Expand Down Expand Up @@ -98,6 +99,11 @@
package_scan_view,
name="package_scan",
),
path(
"packages/<str:dataspace>/<uuid:uuid>/scan_progress_htmx/",
get_scan_progress_htmx_view,
name="scan_progress_htmx",
),
path(
"packages/<str:dataspace>/<uuid:uuid>/tab_scan/",
PackageTabScanView.as_view(),
Expand Down
62 changes: 62 additions & 0 deletions component_catalog/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.shortcuts import render
from django.urls import reverse
from django.urls import reverse_lazy
from django.utils.dateparse import parse_datetime
Expand All @@ -39,6 +40,7 @@
from django.utils.translation import gettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.http import require_GET
from django.views.decorators.http import require_POST
from django.views.generic import FormView
from django.views.generic.edit import BaseFormView
Expand Down Expand Up @@ -85,6 +87,7 @@
from dje.utils import get_help_text as ght
from dje.utils import get_preserved_filters
from dje.utils import is_available
from dje.utils import is_hx_request
from dje.utils import is_uuid4
from dje.utils import localized_datetime
from dje.utils import remove_empty_values
Expand Down Expand Up @@ -1456,6 +1459,7 @@ def package_scan_view(request, dataspace, uuid):
dataspace = user.dataspace
package = get_object_or_404(Package, uuid=uuid, dataspace=dataspace)
download_url = package.download_url
is_hxr = is_hx_request(request)

scancodeio = ScanCodeIO(dataspace)
if scancodeio.is_configured() and dataspace.enable_package_scanning:
Expand All @@ -1466,15 +1470,73 @@ def package_scan_view(request, dataspace, uuid):
user_uuid=user.uuid,
dataspace_uuid=dataspace.uuid,
)

if is_hxr:
template = "product_portfolio/tables/scan_progress_cell.html"
scan = scancodeio.get_scan_results(
download_url=download_url,
dataspace=dataspace,
)
scan["download_result_url"] = get_scan_results_as_file_url(scan)

status = scancodeio.get_status_from_scan_results(scan)
needs_refresh = False
if status in ["running", "not_started", "queued"]:
needs_refresh = True

context = {
"package": package,
"scan": scan,
"view_url": package.get_absolute_url(),
"needs_refresh": needs_refresh,
}
return render(request, template, context)

scancode_msg = "The Package URL was submitted to ScanCode.io for scanning."
messages.success(request, scancode_msg)
else:
if is_hxr:
return HttpResponse("URL is not reachable.")

scancode_msg = f"The URL {download_url} is not reachable."
messages.error(request, scancode_msg)

if is_hxr:
return Http404

return redirect(f"{package.details_url}#scan")


@login_required
@require_GET
def get_scan_progress_htmx_view(request, dataspace, uuid):
template = "product_portfolio/tables/scan_progress_cell.html"
dataspace = request.user.dataspace
package = get_object_or_404(Package, uuid=uuid, dataspace=dataspace)
scancodeio = ScanCodeIO(dataspace)

scan = scancodeio.get_scan_results(
download_url=package.download_url,
dataspace=dataspace,
)
if not scan:
raise Http404("Scan not found.")

status = scancodeio.get_status_from_scan_results(scan)
needs_refresh = False
if status in ["running", "not_started", "queued"]:
needs_refresh = True

scan["download_result_url"] = get_scan_results_as_file_url(scan)
context = {
"package": package,
"scan": scan,
"view_url": package.get_absolute_url(),
"needs_refresh": needs_refresh,
}
return render(request, template, context)


@login_required
@require_POST
def package_create_ajax_view(request):
Expand Down
5 changes: 5 additions & 0 deletions dejacode/static/css/dejacode_bootstrap.css
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ table.text-break thead {
.bg-warning-orange {
background-color: var(--bs-orange);
}
.spinner-border-md {
--bs-spinner-width: 1.5rem;
--bs-spinner-height: 1.5rem;
--bs-spinner-border-width: 0.2em;
}

/* -- Dark there fixes -- */
[data-bs-theme=dark] .btn-outline-dark {
Expand Down
7 changes: 7 additions & 0 deletions dejacode_toolkit/scancodeio.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,13 @@ def delete_scan(self, detail_url):
logger.debug(f"{self.label} [Exception] {exception}")
return False

@staticmethod
def get_status_from_scan_results(scan_results):
status = ""
if runs := scan_results.get("runs"):
status = runs[0].get("status", "")
return status

def update_from_scan(self, package, user):
"""
Update the provided `package` instance using values from Scan results.
Expand Down
5 changes: 5 additions & 0 deletions dje/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,11 @@ def localized_datetime(datetime):
return date_format(dt, default_format)


def is_hx_request(request):
"""Return True if the request is made from HTMX."""
return request.headers.get("HX-Request") == "true"


def merge_common_non_empty_values(dicts):
"""
Merge a list of dictionaries by extracting only the key-value pairs
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{% load i18n %}
<div id="scan-delete-modal" class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Delete {% trans 'Scan' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body bg-body-tertiary">
<p><strong>Are you sure you want to delete this Scan?</strong></p>
<p>
All data and results related to the Scan will be deleted.<br>
If the Scan is in progress, it will be canceled.
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Cancel
</button>
<button hx-swap="outerHTML"
hx-on--before-request="
let target = document.querySelector(this.getAttribute('hx-target'));
if (target) target.innerHTML = 'Deleting...';
"
hx-on--error="
let target = document.querySelector(this.getAttribute('hx-target'));
if (target) target.innerHTML = 'Error!';
"
class="btn btn-danger delete-confirm"
data-bs-dismiss="modal">
Delete Scan
</button>
</div>
</div>
</div>
</div>
Loading
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载