diff --git a/dejacode/static/css/dejacode_bootstrap.css b/dejacode/static/css/dejacode_bootstrap.css index c344a404..bfdd7546 100644 --- a/dejacode/static/css/dejacode_bootstrap.css +++ b/dejacode/static/css/dejacode_bootstrap.css @@ -376,6 +376,20 @@ table.vulnerabilities-table .column-summary { max-width: 300px; } +/* -- Vulnerability tab -- */ +#tab_vulnerabilities .column-vulnerability_id { + width: 210px; +} +#tab_vulnerabilities .column-aliases { + width: 210px; +} +#tab_vulnerabilities .column-max_score { + width: 105px; +} +#tab_vulnerabilities .column-column-affected_packages { + width: 320px; +} + /* -- Package Details -- */ textarea.licenseexpressionwidget { height: 62px; diff --git a/dje/templates/tabs/pagination.html b/dje/templates/tabs/pagination.html new file mode 100644 index 00000000..eb0dcc39 --- /dev/null +++ b/dje/templates/tabs/pagination.html @@ -0,0 +1,30 @@ +{% load humanize %} +
+
+ +
+ {% include 'includes/filters_breadcrumbs.html' with filterset=filterset fragment=tab_id only %} +
+
+
+ {% include 'pagination/object_list_pagination.html' with hx_target=tab_id_html %} +
+
\ No newline at end of file diff --git a/dje/views.py b/dje/views.py index 59b6140c..76d5b2c6 100644 --- a/dje/views.py +++ b/dje/views.py @@ -294,10 +294,11 @@ def get_context_data(self, **kwargs): class TableHeaderMixin: table_headers = () model = None - filterset = None + table_model = None def get_table_headers(self): - opts = self.model._meta + model = self.table_model or self.model + opts = model._meta sort_fields = [] if self.filterset and "sort" in self.filterset.filters: diff --git a/product_portfolio/templates/product_portfolio/tabs/tab_dependencies.html b/product_portfolio/templates/product_portfolio/tabs/tab_dependencies.html index 185294c2..1eb04b59 100644 --- a/product_portfolio/templates/product_portfolio/tabs/tab_dependencies.html +++ b/product_portfolio/templates/product_portfolio/tabs/tab_dependencies.html @@ -3,38 +3,8 @@ {% load humanize %} {% spaceless %} -
-
- -
- {% include 'includes/filters_breadcrumbs.html' with filterset=filter_dependency fragment=tab_id only %} -
-
-
- {% include 'pagination/object_list_pagination.html' with hx_target=tab_id_html %} -
-
- +{% include 'tabs/pagination.html' %} - {% include 'includes/object_list_table_header.html' %} @@ -163,7 +133,7 @@
@@ -42,7 +12,7 @@ {% trans 'For package' %}
- {{ filter_dependency.form.for_package }} + {{ filterset.form.for_package }}
@@ -50,7 +20,7 @@ {% trans 'Resolved to package' %}
- {{ filter_dependency.form.resolved_to_package }} + {{ filterset.form.resolved_to_package }}
@@ -73,7 +43,7 @@ {% trans 'Runtime' %}
- {{ filter_dependency.form.is_runtime }} + {{ filterset.form.is_runtime }}
@@ -81,7 +51,7 @@ {% trans 'Optional' %}
- {{ filter_dependency.form.is_optional }} + {{ filterset.form.is_optional }}
@@ -89,7 +59,7 @@ {% trans 'Pinned' %}
- {{ filter_dependency.form.is_resolved }} + {{ filterset.form.is_resolved }}
No results. - {% if filter_dependency.is_active %} + {% if filterset.is_active %} Clear search and filters diff --git a/product_portfolio/templates/product_portfolio/tabs/tab_vulnerabilities.html b/product_portfolio/templates/product_portfolio/tabs/tab_vulnerabilities.html index 864ef87d..08656605 100644 --- a/product_portfolio/templates/product_portfolio/tabs/tab_vulnerabilities.html +++ b/product_portfolio/templates/product_portfolio/tabs/tab_vulnerabilities.html @@ -1,34 +1,8 @@ {% load i18n %} +{% include 'tabs/pagination.html' %} +{{ table_headers }} - - - - - - - - - + {% include 'includes/object_list_table_header.html' %} {% for vulnerability in page_obj.object_list %} diff --git a/product_portfolio/views.py b/product_portfolio/views.py index f361d030..34c0958d 100644 --- a/product_portfolio/views.py +++ b/product_portfolio/views.py @@ -39,6 +39,7 @@ from django.urls import reverse from django.utils.decorators import method_decorator from django.utils.html import format_html +from django.utils.translation import gettext_lazy as _ from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST from django.views.generic import DetailView @@ -60,6 +61,7 @@ from component_catalog.license_expression_dje import parse_expression from component_catalog.models import Component from component_catalog.models import Package +from component_catalog.models import Vulnerability from dejacode_toolkit.purldb import PurlDB from dejacode_toolkit.scancodeio import ScanCodeIO from dejacode_toolkit.scancodeio import get_hash_uid @@ -94,6 +96,7 @@ from dje.views import SendAboutFilesView from dje.views import TabContentView from dje.views import TabField +from dje.views import TableHeaderMixin from dje.views_formset import FormSetView from license_library.filters import LicenseFilterSet from license_library.models import License @@ -126,7 +129,7 @@ from product_portfolio.models import ScanCodeProject -class BaseProductView: +class BaseProductView: # TODO: Rename this, it is a mixin model = Product slug_url_kwarg = ("name", "version") @@ -1044,7 +1047,7 @@ def get_context_data(self, **kwargs): context_data.update( { - "filter_dependency": filter_dependency, + "filterset": filter_dependency, "page_obj": page_obj, "total_count": product.dependencies.count(), "search_query": self.request.GET.get("dependencies-q", ""), @@ -1068,16 +1071,25 @@ class ProductTabVulnerabilitiesView( LoginRequiredMixin, BaseProductView, PreviousNextPaginationMixin, + TableHeaderMixin, TabContentView, ): - # TODO: Remove duplication + # TODO: Remove duplication + check queries: assertMax template_name = "product_portfolio/tabs/tab_vulnerabilities.html" - paginate_by = 50 + paginate_by = 5 query_dict_page_param = "vulnerabilities-page" tab_id = "vulnerabilities" + table_model = Vulnerability + filterset_class = VulnerabilityFilterSet + table_headers = ( + Header("vulnerability_id", _("Vulnerability")), + Header("aliases", _("Aliases")), + Header("max_score", _("Score"), help_text="Severity score range", filter="max_score"), + Header("summary", _("Summary")), + Header("affected_packages", _("Affected packages"), help_text="Affected product packages"), + ) def get_context_data(self, **kwargs): - context_data = super().get_context_data(**kwargs) product = self.object base_vulnerability_qs = product.get_vulnerability_qs() total_count = base_vulnerability_qs.count() @@ -1090,20 +1102,24 @@ def get_context_data(self, **kwargs): "-min_score", ) - filter_vulnerability = VulnerabilityFilterSet( + # TODO: Add missing anchor + self.filterset = self.filterset_class( self.request.GET, queryset=vulnerability_qs, dataspace=product.dataspace, prefix=self.tab_id, ) - paginator = Paginator(filter_vulnerability.qs, self.paginate_by) + # The self.filterset needs to be set before calling super() + context_data = super().get_context_data(**kwargs) + + paginator = Paginator(self.filterset.qs, self.paginate_by) page_number = self.request.GET.get(self.query_dict_page_param) page_obj = paginator.get_page(page_number) context_data.update( { - "filter_vulnerability": filter_vulnerability, + "filterset": self.filterset, "page_obj": page_obj, "total_count": total_count, "search_query": self.request.GET.get("vulnerabilities-q", ""),
- - {% trans 'Affected by' %} - - - - {% trans 'Aliases' %} - - - - {% trans 'Score' %} - - - - {% trans 'Summary' %} - - - - {% trans 'Affected packages' %} - -