diff --git a/passbook/admin/templates/administration/overview.html b/passbook/admin/templates/administration/overview.html index bc586ce7c..1c4cd1a4f 100644 --- a/passbook/admin/templates/administration/overview.html +++ b/passbook/admin/templates/administration/overview.html @@ -120,7 +120,17 @@
- {{ version }} + {% if version >= version_latest %} + + {% blocktrans with version=version %} + {{ version }} (Up-to-date!) + {% endblocktrans %} + {% else %} + + {% blocktrans with version=version latest=version_latest %} + {{ version }} ({{ latest }} is available!) + {% endblocktrans %} + {% endif %}
diff --git a/passbook/admin/views/overview.py b/passbook/admin/views/overview.py index 83d0ea009..5c049cf7d 100644 --- a/passbook/admin/views/overview.py +++ b/passbook/admin/views/overview.py @@ -1,7 +1,11 @@ """passbook administration overview""" +from functools import lru_cache + from django.core.cache import cache from django.shortcuts import redirect, reverse from django.views.generic import TemplateView +from packaging.version import Version, parse +from requests import RequestException, get from passbook import __version__ from passbook.admin.mixins import AdminRequiredMixin @@ -12,6 +16,19 @@ from passbook.root.celery import CELERY_APP from passbook.stages.invitation.models import Invitation +@lru_cache +def latest_version() -> Version: + """Get latest release from GitHub, cached""" + try: + data = get( + "https://api.github.com/repos/beryju/passbook/releases/latest" + ).json() + tag_name = data.get("tag_name") + return parse(tag_name.split("/")[1]) + except RequestException: + return parse("0.0.0") + + class AdministrationOverviewView(AdminRequiredMixin, TemplateView): """Overview View""" @@ -33,7 +50,8 @@ class AdministrationOverviewView(AdminRequiredMixin, TemplateView): kwargs["stage_count"] = len(Stage.objects.all()) kwargs["flow_count"] = len(Flow.objects.all()) kwargs["invitation_count"] = len(Invitation.objects.all()) - kwargs["version"] = __version__ + kwargs["version"] = parse(__version__) + kwargs["version_latest"] = latest_version() kwargs["worker_count"] = len(CELERY_APP.control.ping(timeout=0.5)) kwargs["providers_without_application"] = Provider.objects.filter( application=None diff --git a/passbook/api/v2/urls.py b/passbook/api/v2/urls.py index 0ff9668b7..256cdb589 100644 --- a/passbook/api/v2/urls.py +++ b/passbook/api/v2/urls.py @@ -32,8 +32,8 @@ from passbook.sources.ldap.api import LDAPPropertyMappingViewSet, LDAPSourceView from passbook.sources.oauth.api import OAuthSourceViewSet from passbook.sources.saml.api import SAMLSourceViewSet from passbook.stages.captcha.api import CaptchaStageViewSet -from passbook.stages.dummy.api import DummyStageViewSet from passbook.stages.consent.api import ConsentStageViewSet +from passbook.stages.dummy.api import DummyStageViewSet from passbook.stages.email.api import EmailStageViewSet from passbook.stages.identification.api import IdentificationStageViewSet from passbook.stages.invitation.api import InvitationStageViewSet, InvitationViewSet diff --git a/swagger.yaml b/swagger.yaml index 8a11bd93f..c5833bff9 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -3275,6 +3275,133 @@ paths: required: true type: string format: uuid + /stages/consent/: + get: + operationId: stages_consent_list + description: ConsentStage Viewset + parameters: + - name: ordering + in: query + description: Which field to use when ordering the results. + required: false + type: string + - name: search + in: query + description: A search term. + required: false + type: string + - name: limit + in: query + description: Number of results to return per page. + required: false + type: integer + - name: offset + in: query + description: The initial index from which to return the results. + required: false + type: integer + responses: + '200': + description: '' + schema: + required: + - count + - results + type: object + properties: + count: + type: integer + next: + type: string + format: uri + x-nullable: true + previous: + type: string + format: uri + x-nullable: true + results: + type: array + items: + $ref: '#/definitions/ConsentStage' + tags: + - stages + post: + operationId: stages_consent_create + description: ConsentStage Viewset + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/ConsentStage' + responses: + '201': + description: '' + schema: + $ref: '#/definitions/ConsentStage' + tags: + - stages + parameters: [] + /stages/consent/{stage_uuid}/: + get: + operationId: stages_consent_read + description: ConsentStage Viewset + parameters: [] + responses: + '200': + description: '' + schema: + $ref: '#/definitions/ConsentStage' + tags: + - stages + put: + operationId: stages_consent_update + description: ConsentStage Viewset + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/ConsentStage' + responses: + '200': + description: '' + schema: + $ref: '#/definitions/ConsentStage' + tags: + - stages + patch: + operationId: stages_consent_partial_update + description: ConsentStage Viewset + parameters: + - name: data + in: body + required: true + schema: + $ref: '#/definitions/ConsentStage' + responses: + '200': + description: '' + schema: + $ref: '#/definitions/ConsentStage' + tags: + - stages + delete: + operationId: stages_consent_delete + description: ConsentStage Viewset + parameters: [] + responses: + '204': + description: '' + tags: + - stages + parameters: + - name: stage_uuid + in: path + description: A UUID string identifying this Consent Stage. + required: true + type: string + format: uuid /stages/dummy/: get: operationId: stages_dummy_list @@ -6052,6 +6179,20 @@ definitions: description: Private key, acquired from https://www.google.com/recaptcha/intro/v3.html type: string minLength: 1 + ConsentStage: + required: + - name + type: object + properties: + pk: + title: Stage uuid + type: string + format: uuid + readOnly: true + name: + title: Name + type: string + minLength: 1 DummyStage: required: - name