diff --git a/idhub/admin/views.py b/idhub/admin/views.py
index d0b9405..7bb3f8d 100644
--- a/idhub/admin/views.py
+++ b/idhub/admin/views.py
@@ -8,6 +8,7 @@ from smtplib import SMTPException
from django_tables2 import SingleTableView
from django.conf import settings
+from django.template.loader import get_template
from django.utils.translation import gettext_lazy as _
from django.views.generic.base import TemplateView
from django.views.generic.edit import (
@@ -833,13 +834,13 @@ class SchemasImportView(SchemasMix):
if not Schemas.objects.filter(file_schema=x).exists()]
return schemas
-
+
class SchemasImportAddView(SchemasMix):
def get(self, request, *args, **kwargs):
file_name = kwargs['file_schema']
schemas_files = os.listdir(settings.SCHEMAS_DIR)
- if not file_name in schemas_files:
+ if file_name not in schemas_files:
messages.error(self.request, f"The schema {file_name} not exist!")
return redirect('idhub:admin_schemas_import')
@@ -858,7 +859,10 @@ class SchemasImportAddView(SchemasMix):
except Exception:
messages.error(self.request, _('This is not a valid schema!'))
return
- schema = Schemas.objects.create(file_schema=file_name, data=data, type=name)
+ schema = Schemas.objects.create(file_schema=file_name,
+ data=data, type=name,
+ template_description=self.get_description()
+ )
schema.save()
return schema
@@ -870,6 +874,20 @@ class SchemasImportAddView(SchemasMix):
return data
+ def get_template_description(self):
+ context = self.get_context()
+ template_name = 'credentials/{}'.format(
+ self.schema.file_schema
+ )
+ tmpl = get_template(template_name)
+ return tmpl.render(context)
+
+ def get_description(self):
+ for des in json.loads(self.render()).get('description', []):
+ if settings.LANGUAGE_CODE == des.get('lang'):
+ return des.get('value', '')
+ return ''
+
class ImportView(ImportExport, SingleTableView):
template_name = "idhub/admin/import.html"
diff --git a/idhub/migrations/0002_schemas_template_description_alter_did_label_and_more.py b/idhub/migrations/0002_schemas_template_description_alter_did_label_and_more.py
new file mode 100644
index 0000000..866e23c
--- /dev/null
+++ b/idhub/migrations/0002_schemas_template_description_alter_did_label_and_more.py
@@ -0,0 +1,82 @@
+# Generated by Django 4.2.5 on 2023-12-19 17:27
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ('idhub', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='schemas',
+ name='template_description',
+ field=models.TextField(null=True),
+ ),
+ migrations.AlterField(
+ model_name='did',
+ name='label',
+ field=models.CharField(max_length=50, verbose_name='Label'),
+ ),
+ migrations.AlterField(
+ model_name='event',
+ name='created',
+ field=models.DateTimeField(auto_now=True, verbose_name='Date'),
+ ),
+ migrations.AlterField(
+ model_name='event',
+ name='message',
+ field=models.CharField(max_length=350, verbose_name='Description'),
+ ),
+ migrations.AlterField(
+ model_name='event',
+ name='type',
+ field=models.PositiveSmallIntegerField(
+ choices=[
+ (1, 'User registered'),
+ (2, 'User welcomed'),
+ (3, 'Data update requested by user'),
+ (4, 'Data update requested. Pending approval by administrator'),
+ (5, "User's data updated by admin"),
+ (6, 'Your data updated by admin'),
+ (7, 'User deactivated by admin'),
+ (8, 'DID created by user'),
+ (9, 'DID created'),
+ (10, 'DID deleted'),
+ (11, 'Credential deleted by user'),
+ (12, 'Credential deleted'),
+ (13, 'Credential issued for user'),
+ (14, 'Credential issued'),
+ (15, 'Credential presented by user'),
+ (16, 'Credential presented'),
+ (17, 'Credential enabled'),
+ (18, 'Credential available'),
+ (19, 'Credential revoked by admin'),
+ (20, 'Credential revoked'),
+ (21, 'Role created by admin'),
+ (22, 'Role modified by admin'),
+ (23, 'Role deleted by admin'),
+ (24, 'Service created by admin'),
+ (25, 'Service modified by admin'),
+ (26, 'Service deleted by admin'),
+ (27, 'Organisational DID created by admin'),
+ (28, 'Organisational DID deleted by admin'),
+ (29, 'User deactivated'),
+ (30, 'User activated'),
+ ],
+ verbose_name='Event',
+ ),
+ ),
+ migrations.AlterField(
+ model_name='userrol',
+ name='service',
+ field=models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name='users',
+ to='idhub.service',
+ verbose_name='Service',
+ ),
+ ),
+ ]
diff --git a/idhub/models.py b/idhub/models.py
index 83dd90a..40b0b5d 100644
--- a/idhub/models.py
+++ b/idhub/models.py
@@ -438,6 +438,7 @@ class Schemas(models.Model):
created_at = models.DateTimeField(auto_now=True)
_name = models.CharField(max_length=250, null=True, db_column='name')
_description = models.CharField(max_length=250, null=True, db_column='description')
+ template_description = models.TextField(null=True)
@property
def get_schema(self):
@@ -518,11 +519,8 @@ class VerificableCredential(models.Model):
def type(self):
return self.schema.type
- def description(self):
- for des in json.loads(self.render()).get('description', []):
- if settings.LANGUAGE_CODE == des.get('lang'):
- return des.get('value', '')
- return ''
+ def get_description(self):
+ return self.schema.template_description
def get_status(self):
return self.Status(self.status).label
@@ -569,7 +567,6 @@ class VerificableCredential(models.Model):
tmpl = get_template(template_name)
return tmpl.render(context)
-
def get_issued_on(self):
if self.issued_on:
return self.issued_on.strftime("%m/%d/%Y")
@@ -637,7 +634,7 @@ class Service(models.Model):
def get_roles(self):
if self.rol.exists():
- return ", ".join([x.name for x in self.rol.all()])
+ return ", ".join([x.name for x in self.rol.order_by("name")])
return _("None")
def __str__(self):
diff --git a/idhub/templates/idhub/user/credentials.html b/idhub/templates/idhub/user/credentials.html
index 6f68e56..fcb0378 100644
--- a/idhub/templates/idhub/user/credentials.html
+++ b/idhub/templates/idhub/user/credentials.html
@@ -1,39 +1,11 @@
{% extends "idhub/base.html" %}
{% load i18n %}
+{% load render_table from django_tables2 %}
{% block content %}
{{ subtitle }}
-
-
-
-
-
-
- |
- |
- |
- |
- |
-
-
-
- {% for f in credentials.all %}
-
- {{ f.type }} |
- {{ f.description }} |
- {{ f.get_issued_on }} |
- {{ f.get_status }} |
-
-
- |
-
- {% endfor %}
-
-
-
-
-
+{% render_table table %}
{% endblock %}
diff --git a/idhub/templates/idhub/user/dashboard.html b/idhub/templates/idhub/user/dashboard.html
index c13f776..22777ee 100644
--- a/idhub/templates/idhub/user/dashboard.html
+++ b/idhub/templates/idhub/user/dashboard.html
@@ -1,29 +1,12 @@
{% extends "idhub/base.html" %}
{% load i18n %}
+{% load render_table from django_tables2 %}
{% block content %}
{{ subtitle }}
-
-
-
-
- |
- |
- |
-
-
-
- {% for ev in user.events.all %}
-
- {{ ev.get_type_name }} |
- {{ ev.message }} |
- {{ ev.created }} |
-
- {% endfor %}
-
-
-
+{% render_table table %}
+
{% endblock %}
diff --git a/idhub/templates/idhub/user/dids.html b/idhub/templates/idhub/user/dids.html
index 28177e2..dc3bfc4 100644
--- a/idhub/templates/idhub/user/dids.html
+++ b/idhub/templates/idhub/user/dids.html
@@ -1,41 +1,15 @@
{% extends "idhub/base.html" %}
{% load i18n %}
+{% load render_table from django_tables2 %}
{% block content %}
{{ subtitle }}
-
-
-
-
-
-
- |
- |
- |
- |
- |
-
-
-
- {% for d in dids.all %}
-
- {{ d.created_at }} |
- {{ d.label }} |
- {{ d.did }} |
- |
- |
-
- {% endfor %}
-
-
-
-
-
+{% render_table table %}
+
{% for d in dids.all %}
diff --git a/idhub/templates/idhub/user/profile.html b/idhub/templates/idhub/user/profile.html
index 3a5d5f3..6efdc48 100644
--- a/idhub/templates/idhub/user/profile.html
+++ b/idhub/templates/idhub/user/profile.html
@@ -1,5 +1,6 @@
{% extends "idhub/base.html" %}
{% load i18n %}
+{% load render_table from django_tables2 %}
{% block content %}
@@ -33,33 +34,6 @@
{% bootstrap_form form %}
+{% render_table table %}
-
-
-
-
-
-
- |
- |
- |
- |
-
-
-
- {% for membership in object.memberships.all %}
-
- {{ membership.get_type }} |
- {{ membership.start_date|default:'' }} |
- {{ membership.end_date|default:'' }} |
-
-
- |
-
- {% endfor %}
-
-
-
-
-
{% endblock %}
diff --git a/idhub/templates/idhub/user/roles.html b/idhub/templates/idhub/user/roles.html
index 2c32e97..22777ee 100644
--- a/idhub/templates/idhub/user/roles.html
+++ b/idhub/templates/idhub/user/roles.html
@@ -1,35 +1,12 @@
{% extends "idhub/base.html" %}
{% load i18n %}
+{% load render_table from django_tables2 %}
{% block content %}
{{ subtitle }}
-
-
-
-
-
-
- |
- |
- |
-
-
-
- {% for rol in user.roles.all %}
-
- {{ rol.service.get_roles }} |
- {{ rol.service.description }} |
- {{ rol.service.domain }} |
-
- {% endfor %}
-
-
-
-
-
-
+{% render_table table %}
{% endblock %}
diff --git a/idhub/user/forms.py b/idhub/user/forms.py
index 5ac04ad..07b134d 100644
--- a/idhub/user/forms.py
+++ b/idhub/user/forms.py
@@ -1,21 +1,10 @@
-import requests
from django import forms
from django.conf import settings
from django.utils.translation import gettext_lazy as _
-
-from idhub_auth.models import User
from idhub.models import DID, VerificableCredential
from oidc4vp.models import Organization
-class ProfileForm(forms.ModelForm):
- MANDATORY_FIELDS = ['first_name', 'last_name', 'email']
-
- class Meta:
- model = User
- fields = ('first_name', 'last_name', 'email')
-
-
class RequestCredentialForm(forms.Form):
did = forms.ChoiceField(label=_("Did"), choices=[])
credential = forms.ChoiceField(label=_("Credential"), choices=[])
diff --git a/idhub/user/tables.py b/idhub/user/tables.py
new file mode 100644
index 0000000..0cdb4fb
--- /dev/null
+++ b/idhub/user/tables.py
@@ -0,0 +1,144 @@
+from django.utils.html import format_html
+import django_tables2 as tables
+from idhub.models import Event, Membership, UserRol, DID, VerificableCredential
+
+
+class ButtonColumn(tables.Column):
+ attrs = {
+ "a": {
+ "type": "button",
+ "class": "text-primary",
+ }
+ }
+ # it makes no sense to order a column of buttons
+ orderable = False
+ # django_tables will only call the render function if it doesn't find
+ # any empty values in the data, so we stop it from matching the data
+ # to any value considered empty
+ empty_values = ()
+
+
+class DashboardTable(tables.Table):
+ type = tables.Column(verbose_name="Event")
+ message = tables.Column(verbose_name="Description")
+ created = tables.Column(verbose_name="Date")
+
+ class Meta:
+ model = Event
+ template_name = "idhub/custom_table.html"
+ fields = ("type", "message", "created")
+
+
+class PersonalInfoTable(tables.Table):
+ type = tables.Column(verbose_name="Membership")
+ credential = ButtonColumn(
+ # TODO: See where this should actually link
+ linkify="idhub:user_credentials",
+ orderable=False
+ )
+
+ def render_credential(self):
+ return format_html('
')
+
+ class Meta:
+ model = Membership
+ template_name = "idhub/custom_table.html"
+ fields = ("type", "start_date", "end_date", "credential")
+
+
+class RolesTable(tables.Table):
+ name = tables.Column(verbose_name="Role", empty_values=())
+ description = tables.Column(empty_values=())
+ service = tables.Column()
+
+ def render_name(self, record):
+ return record.service.get_roles()
+
+ def render_description(self, record):
+ return record.service.description
+
+ def render_service(self, record):
+ return record.service.domain
+
+ def order_name(self, queryset, is_descending):
+ queryset = queryset.order_by(
+ ("-" if is_descending else "") + "service__rol__name"
+ )
+
+ return (queryset, True)
+
+ def order_description(self, queryset, is_descending):
+ queryset = queryset.order_by(
+ ("-" if is_descending else "") + "service__description"
+ )
+
+ return (queryset, True)
+
+ def order_service(self, queryset, is_descending):
+ queryset = queryset.order_by(
+ ("-" if is_descending else "") + "service__domain"
+ )
+
+ return (queryset, True)
+
+ class Meta:
+ model = UserRol
+ template_name = "idhub/custom_table.html"
+ fields = ("name", "description", "service")
+
+
+class DIDTable(tables.Table):
+ created_at = tables.Column(verbose_name="Date")
+ did = tables.Column(verbose_name="ID")
+ edit = ButtonColumn(
+ linkify={
+ "viewname": "idhub:user_dids_edit",
+ "args": [tables.A("pk")]
+ },
+ orderable=False
+ )
+ delete_template_code = """
"""
+ delete = tables.TemplateColumn(template_code=delete_template_code,
+ orderable=False)
+
+ def render_edit(self):
+ return format_html('
')
+
+ class Meta:
+ model = DID
+ template_name = "idhub/custom_table.html"
+ fields = ("created_at", "label", "did", "edit", "delete")
+
+
+class CredentialsTable(tables.Table):
+ description = tables.Column(verbose_name="Details", empty_values=())
+
+ def render_description(self, record):
+ return record.get_description()
+
+ def render_status(self, record):
+ return record.get_status()
+
+ def order_type(self, queryset, is_descending):
+ queryset = queryset.order_by(
+ ("-" if is_descending else "") + "schema__type"
+ )
+
+ return (queryset, True)
+
+ def order_description(self, queryset, is_descending):
+ queryset = queryset.order_by(
+ ("-" if is_descending else "") + "schema__template_description"
+ )
+
+ return (queryset, True)
+
+ class Meta:
+ model = VerificableCredential
+ template_name = "idhub/custom_table.html"
+ fields = ("type", "description", "issued_on", "status")
diff --git a/idhub/user/views.py b/idhub/user/views.py
index e6e28dc..07430f5 100644
--- a/idhub/user/views.py
+++ b/idhub/user/views.py
@@ -12,13 +12,21 @@ from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse_lazy
from django.http import HttpResponse
from django.contrib import messages
+from django_tables2 import SingleTableView
+from idhub.user.tables import (
+ DashboardTable,
+ PersonalInfoTable,
+ RolesTable,
+ DIDTable,
+ CredentialsTable
+)
from idhub.user.forms import (
- ProfileForm,
- RequestCredentialForm,
- DemandAuthorizationForm
+ RequestCredentialForm,
+ DemandAuthorizationForm
)
from idhub.mixins import UserView
-from idhub.models import DID, VerificableCredential, Event
+from idhub.models import DID, VerificableCredential, Event, Membership
+from idhub_auth.models import User
class MyProfile(UserView):
@@ -31,21 +39,35 @@ class MyWallet(UserView):
section = "MyWallet"
-class DashboardView(UserView, TemplateView):
+class DashboardView(UserView, SingleTableView):
template_name = "idhub/user/dashboard.html"
+ table_class = DashboardTable
title = _('Dashboard')
subtitle = _('Events')
icon = 'bi bi-bell'
section = "Home"
+ def get_queryset(self, **kwargs):
+ queryset = Event.objects.select_related('user').filter(
+ user=self.request.user)
-class ProfileView(MyProfile, UpdateView):
+ return queryset
+
+
+class ProfileView(MyProfile, UpdateView, SingleTableView):
template_name = "idhub/user/profile.html"
+ table_class = PersonalInfoTable
subtitle = _('My personal data')
icon = 'bi bi-person-gear'
- from_class = ProfileForm
fields = ('first_name', 'last_name', 'email')
success_url = reverse_lazy('idhub:user_profile')
+ model = User
+
+ def get_queryset(self, **kwargs):
+ queryset = Membership.objects.select_related('user').filter(
+ user=self.request.user)
+
+ return queryset
def get_object(self):
return self.request.user
@@ -61,11 +83,17 @@ class ProfileView(MyProfile, UpdateView):
return super().form_valid(form)
-class RolesView(MyProfile, TemplateView):
+class RolesView(MyProfile, SingleTableView):
template_name = "idhub/user/roles.html"
+ table_class = RolesTable
subtitle = _('My roles')
icon = 'fa-brands fa-critical-role'
+ def get_queryset(self, **kwargs):
+ queryset = self.request.user.roles.all()
+
+ return queryset
+
class GDPRView(MyProfile, TemplateView):
template_name = "idhub/user/gdpr.html"
@@ -73,20 +101,17 @@ class GDPRView(MyProfile, TemplateView):
icon = 'bi bi-file-earmark-medical'
-class CredentialsView(MyWallet, TemplateView):
+class CredentialsView(MyWallet, SingleTableView):
template_name = "idhub/user/credentials.html"
+ table_class = CredentialsTable
subtitle = _('Credential management')
icon = 'bi bi-patch-check-fill'
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- creds = VerificableCredential.objects.filter(
- user=self.request.user
- )
- context.update({
- 'credentials': creds,
- })
- return context
+ def get_queryset(self):
+ queryset = VerificableCredential.objects.filter(
+ user=self.request.user)
+
+ return queryset
class CredentialView(MyWallet, TemplateView):
@@ -180,8 +205,9 @@ class DemandAuthorizationView(MyWallet, FormView):
return super().form_valid(form)
-class DidsView(MyWallet, TemplateView):
+class DidsView(MyWallet, SingleTableView):
template_name = "idhub/user/dids.html"
+ table_class = DIDTable
subtitle = _('Identities (DIDs)')
icon = 'bi bi-patch-check-fill'
@@ -192,6 +218,11 @@ class DidsView(MyWallet, TemplateView):
})
return context
+ def get_queryset(self, **kwargs):
+ queryset = DID.objects.filter(user=self.request.user)
+
+ return queryset
+
class DidRegisterView(MyWallet, CreateView):
template_name = "idhub/user/did_register.html"