Compare commits
No commits in common. "69cfc3877159bd532fd58eaa4c4d25151ac46e58" and "aed28a8a531224dff400a69e82ef7ad31244d949" have entirely different histories.
69cfc38771
...
aed28a8a53
|
@ -21,7 +21,6 @@ dependencies = [
|
||||||
"whitenoise==6.6.0",
|
"whitenoise==6.6.0",
|
||||||
"django-zen-queries==2.1.0",
|
"django-zen-queries==2.1.0",
|
||||||
"django-registries==0.0.3",
|
"django-registries==0.0.3",
|
||||||
"django-view-decorator==0.0.4",
|
|
||||||
]
|
]
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ django==5.0.1
|
||||||
# django-allauth
|
# django-allauth
|
||||||
# django-money
|
# django-money
|
||||||
# django-registries
|
# django-registries
|
||||||
# django-view-decorator
|
|
||||||
# django-zen-queries
|
# django-zen-queries
|
||||||
# membersystem (pyproject.toml)
|
# membersystem (pyproject.toml)
|
||||||
django-allauth==0.60.0
|
django-allauth==0.60.0
|
||||||
|
@ -41,8 +40,6 @@ django-money==3.4.1
|
||||||
# via membersystem (pyproject.toml)
|
# via membersystem (pyproject.toml)
|
||||||
django-registries==0.0.3
|
django-registries==0.0.3
|
||||||
# via membersystem (pyproject.toml)
|
# via membersystem (pyproject.toml)
|
||||||
django-view-decorator==0.0.4
|
|
||||||
# via membersystem (pyproject.toml)
|
|
||||||
django-zen-queries==2.1.0
|
django-zen-queries==2.1.0
|
||||||
# via membersystem (pyproject.toml)
|
# via membersystem (pyproject.toml)
|
||||||
environs[django]==10.0.0
|
environs[django]==10.0.0
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
# Generated by Django 5.0.1 on 2024-01-14 11:14
|
|
||||||
|
|
||||||
import djmoney.models.fields
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
dependencies = [
|
|
||||||
("accounting", "0001_initial"),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="order",
|
|
||||||
name="price_currency",
|
|
||||||
field=djmoney.models.fields.CurrencyField(
|
|
||||||
choices=[("DKK", "DKK")], default=None, editable=False, max_length=3
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="order",
|
|
||||||
name="vat_currency",
|
|
||||||
field=djmoney.models.fields.CurrencyField(
|
|
||||||
choices=[("DKK", "DKK")], default=None, editable=False, max_length=3
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="payment",
|
|
||||||
name="amount_currency",
|
|
||||||
field=djmoney.models.fields.CurrencyField(
|
|
||||||
choices=[("DKK", "DKK")], default=None, editable=False, max_length=3
|
|
||||||
),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name="transaction",
|
|
||||||
name="amount_currency",
|
|
||||||
field=djmoney.models.fields.CurrencyField(
|
|
||||||
choices=[("DKK", "DKK")], default=None, editable=False, max_length=3
|
|
||||||
),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.auth.decorators import permission_required
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django_view_decorator import namespaced_decorator_factory
|
|
||||||
|
|
||||||
from .permissions import ADMINISTRATE_MEMBERS
|
from .permissions import ADMINISTRATE_MEMBERS
|
||||||
from .selectors import get_member
|
from .selectors import get_member
|
||||||
|
@ -11,14 +12,7 @@ from utils.view_utils import render_list
|
||||||
from utils.view_utils import RowAction
|
from utils.view_utils import RowAction
|
||||||
|
|
||||||
|
|
||||||
member_view = namespaced_decorator_factory(namespace="member", base_path="membership")
|
@login_required
|
||||||
|
|
||||||
|
|
||||||
@member_view(
|
|
||||||
paths="",
|
|
||||||
name="membership-overview",
|
|
||||||
login_required=True,
|
|
||||||
)
|
|
||||||
def membership_overview(request):
|
def membership_overview(request):
|
||||||
memberships = get_memberships(member=request.user)
|
memberships = get_memberships(member=request.user)
|
||||||
current_membership = memberships.current()
|
current_membership = memberships.current()
|
||||||
|
@ -39,18 +33,8 @@ def membership_overview(request):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
admin_members_view = namespaced_decorator_factory(
|
@login_required
|
||||||
namespace="admin-members",
|
@permission_required(ADMINISTRATE_MEMBERS.path)
|
||||||
base_path="admin",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@admin_members_view(
|
|
||||||
paths="members/",
|
|
||||||
name="list",
|
|
||||||
login_required=True,
|
|
||||||
permissions=[ADMINISTRATE_MEMBERS.path],
|
|
||||||
)
|
|
||||||
def members_admin(request):
|
def members_admin(request):
|
||||||
users = get_members()
|
users = get_members()
|
||||||
|
|
||||||
|
@ -70,19 +54,15 @@ def members_admin(request):
|
||||||
row_actions=[
|
row_actions=[
|
||||||
RowAction(
|
RowAction(
|
||||||
label=_("View"),
|
label=_("View"),
|
||||||
url_name="admin-members:detail",
|
url_name="admin-members-detail",
|
||||||
url_kwargs={"member_id": "id"},
|
url_kwargs={"member_id": "id"},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@admin_members_view(
|
@login_required
|
||||||
paths="<int:member_id>/",
|
@permission_required(ADMINISTRATE_MEMBERS.path)
|
||||||
name="detail",
|
|
||||||
login_required=True,
|
|
||||||
permissions=[ADMINISTRATE_MEMBERS.path],
|
|
||||||
)
|
|
||||||
def members_admin_detail(request, member_id):
|
def members_admin_detail(request, member_id):
|
||||||
member = get_member(member_id=member_id)
|
member = get_member(member_id=member_id)
|
||||||
subscription_periods = get_subscription_periods(member=member)
|
subscription_periods = get_subscription_periods(member=member)
|
||||||
|
@ -90,7 +70,7 @@ def members_admin_detail(request, member_id):
|
||||||
context = {
|
context = {
|
||||||
"member": member,
|
"member": member,
|
||||||
"subscription_periods": subscription_periods,
|
"subscription_periods": subscription_periods,
|
||||||
"base_path": "admin-members:list",
|
"base_path": "admin-members",
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
|
|
|
@ -40,7 +40,6 @@ DJANGO_APPS = [
|
||||||
THIRD_PARTY_APPS = [
|
THIRD_PARTY_APPS = [
|
||||||
"allauth",
|
"allauth",
|
||||||
"allauth.account",
|
"allauth.account",
|
||||||
"django_view_decorator",
|
|
||||||
"django_registries",
|
"django_registries",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ p, h1, h2, h3, h4, h5, h6 {
|
||||||
--light : #fff;
|
--light : #fff;
|
||||||
--light-dust : #f6f6f6;
|
--light-dust : #f6f6f6;
|
||||||
--dust : #f1f1f1;
|
--dust : #f1f1f1;
|
||||||
--medium-dust : #dadada;
|
|
||||||
--dark-dust : #bfbfbf;
|
--dark-dust : #bfbfbf;
|
||||||
--fade : #878787;
|
--fade : #878787;
|
||||||
--twilight : #4a4a4a;
|
--twilight : #4a4a4a;
|
||||||
|
@ -62,8 +61,6 @@ h1, h2, h3, h4, h5, h6 {
|
||||||
|
|
||||||
a {
|
a {
|
||||||
font-weight : 500;
|
font-weight : 500;
|
||||||
color : var(--splash);
|
|
||||||
text-decoration : none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
@ -252,25 +249,6 @@ button:hover {
|
||||||
opacity : 1.0;
|
opacity : 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
article table {
|
|
||||||
width : 100%;
|
|
||||||
border-spacing : 0;
|
|
||||||
margin : var(--space) 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
article table thead th {
|
|
||||||
text-align : left;
|
|
||||||
}
|
|
||||||
|
|
||||||
article table tbody tr:nth-child(odd) {
|
|
||||||
background : var(--medium-dust);
|
|
||||||
}
|
|
||||||
|
|
||||||
article table thead th,
|
|
||||||
article table tbody td {
|
|
||||||
padding : var(--half-space);
|
|
||||||
}
|
|
||||||
|
|
||||||
form > div {
|
form > div {
|
||||||
margin : 0 0 var(--double-space);
|
margin : 0 0 var(--double-space);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url "services:list" %}" class="{% active_path "services:list" "current" %}">
|
<a href="/services" class="{% active_path "services" "current" %}">
|
||||||
Services
|
Services
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
@ -64,7 +64,7 @@
|
||||||
|
|
||||||
{% if perms.membership.administrate_memberships %}
|
{% if perms.membership.administrate_memberships %}
|
||||||
<li>
|
<li>
|
||||||
<a href="{% url "admin-members:list" %}" class="{% active_path "admin-members:list" "current" %}">
|
<a href="{% url "admin-members" %}" class="{% active_path "admin-members" "current" %}">
|
||||||
Admin
|
Admin
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -28,16 +28,11 @@
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<h3>{{ service.name }}</h3>
|
<h3>{{ service.name }}</h3>
|
||||||
<p>{{ service.description }}</p>
|
<p>{{ service.description }}</p>
|
||||||
|
|
||||||
<a href="{% url "services:detail" service_slug=service.slug %}">
|
|
||||||
Read more
|
|
||||||
</a>
|
|
||||||
|
|
|
||||||
<a href="{{ service.url }}" target="_blank">
|
<a href="{{ service.url }}" target="_blank">
|
||||||
Visit
|
Visit
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a href="{% url "services:subscribe" service_slug=service.slug %}">Subscribe</a>
|
<a>Subscribe</a>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
|
@ -1,12 +1,26 @@
|
||||||
"""URLs for the membersystem"""
|
"""URLs for the membersystem"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.urls import include
|
from django.urls import include
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
from django_view_decorator import include_view_urls
|
|
||||||
|
from .views import index
|
||||||
|
from .views import services_overview
|
||||||
|
from membership.views import members_admin
|
||||||
|
from membership.views import members_admin_detail
|
||||||
|
from membership.views import membership_overview
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", include_view_urls(extra_modules=["project.views"])),
|
path("", login_required(index), name="index"),
|
||||||
|
path("services/", login_required(services_overview), name="services"),
|
||||||
|
path("membership/", membership_overview, name="membership-overview"),
|
||||||
|
path("admin/members/", members_admin, name="admin-members"),
|
||||||
|
path(
|
||||||
|
"admin/members/<int:member_id>/",
|
||||||
|
members_admin_detail,
|
||||||
|
name="admin-members-detail",
|
||||||
|
),
|
||||||
path("accounts/", include("allauth.urls")),
|
path("accounts/", include("allauth.urls")),
|
||||||
path("_admin/", admin.site.urls),
|
path("_admin/", admin.site.urls),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,12 +1,35 @@
|
||||||
from django_view_decorator import view
|
from membership.models import ServiceAccess
|
||||||
|
from services.registry import ServiceRegistry
|
||||||
from utils.view_utils import render
|
from utils.view_utils import render
|
||||||
|
|
||||||
|
|
||||||
@view(
|
|
||||||
paths="",
|
|
||||||
name="index",
|
|
||||||
login_required=True,
|
|
||||||
)
|
|
||||||
def index(request):
|
def index(request):
|
||||||
return render(request, "index.html")
|
return render(request, "index.html")
|
||||||
|
|
||||||
|
|
||||||
|
def services_overview(request):
|
||||||
|
active_services = [
|
||||||
|
access.service_implementation
|
||||||
|
for access in ServiceAccess.objects.filter(
|
||||||
|
user=request.user,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
active_service_classes = [service.__class__ for service in active_services]
|
||||||
|
|
||||||
|
services = [
|
||||||
|
service
|
||||||
|
for _, service in ServiceRegistry.get_items()
|
||||||
|
if service not in active_service_classes
|
||||||
|
]
|
||||||
|
|
||||||
|
context = {
|
||||||
|
"non_active_services": services,
|
||||||
|
"active_services": active_services,
|
||||||
|
}
|
||||||
|
|
||||||
|
return render(
|
||||||
|
request=request,
|
||||||
|
template_name="services_overview.html",
|
||||||
|
context=context,
|
||||||
|
)
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
from django import forms
|
|
||||||
from django_registries.registry import Interface
|
from django_registries.registry import Interface
|
||||||
from django_registries.registry import Registry
|
from django_registries.registry import Registry
|
||||||
|
|
||||||
|
@ -24,17 +23,3 @@ class ServiceInterface(Interface):
|
||||||
# - maybe a list of tuples with the field name and the type of the field
|
# - maybe a list of tuples with the field name and the type of the field
|
||||||
# this could be used to generate a form for the service, and also to validate
|
# this could be used to generate a form for the service, and also to validate
|
||||||
# the data saved in a JSONField on the ServiceAccess model
|
# the data saved in a JSONField on the ServiceAccess model
|
||||||
|
|
||||||
subscribe_fields: list[tuple[str, forms.Field]] = []
|
|
||||||
|
|
||||||
def get_form(self) -> type:
|
|
||||||
"""Get the form for the service"""
|
|
||||||
print(self.subscribe_fields)
|
|
||||||
return type(
|
|
||||||
"ServiceForm",
|
|
||||||
(forms.Form,),
|
|
||||||
{
|
|
||||||
field_name: field_type
|
|
||||||
for field_name, field_type in self.subscribe_fields
|
|
||||||
},
|
|
||||||
)()
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from django import forms
|
|
||||||
|
|
||||||
from .registry import ServiceInterface
|
from .registry import ServiceInterface
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,10 +14,6 @@ class MatrixService(ServiceInterface):
|
||||||
url = "https://matrix.data.coop"
|
url = "https://matrix.data.coop"
|
||||||
description = "Matrix service for data.coop"
|
description = "Matrix service for data.coop"
|
||||||
|
|
||||||
subscribe_fields = [
|
|
||||||
("username", forms.CharField()),
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class MastodonService(ServiceInterface):
|
class MastodonService(ServiceInterface):
|
||||||
slug = "mastodon"
|
slug = "mastodon"
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="content-view">
|
|
||||||
<h2>{{ service.name }}</h2>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,18 +0,0 @@
|
||||||
{% extends "base.html" %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="content-view">
|
|
||||||
<h2>Subscribe to {{ service.name }}</h2>
|
|
||||||
|
|
||||||
<form>
|
|
||||||
{{ form }}
|
|
||||||
|
|
||||||
<button type="submit">
|
|
||||||
Subscribe
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
|
|
@ -1,86 +1 @@
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
from django_view_decorator import namespaced_decorator_factory
|
|
||||||
|
|
||||||
from membership.models import ServiceAccess
|
|
||||||
from services.registry import ServiceRegistry
|
|
||||||
from utils.view_utils import render
|
|
||||||
|
|
||||||
|
|
||||||
services_view = namespaced_decorator_factory(
|
|
||||||
namespace="services",
|
|
||||||
base_path="services",
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@services_view(
|
|
||||||
paths="",
|
|
||||||
name="list",
|
|
||||||
login_required=True,
|
|
||||||
)
|
|
||||||
def services_overview(request):
|
|
||||||
active_services = [
|
|
||||||
access.service_implementation
|
|
||||||
for access in ServiceAccess.objects.filter(
|
|
||||||
user=request.user,
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
active_service_classes = [service.__class__ for service in active_services]
|
|
||||||
|
|
||||||
services = [
|
|
||||||
service
|
|
||||||
for _, service in ServiceRegistry.get_items()
|
|
||||||
if service not in active_service_classes
|
|
||||||
]
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"non_active_services": services,
|
|
||||||
"active_services": active_services,
|
|
||||||
}
|
|
||||||
|
|
||||||
return render(
|
|
||||||
request=request,
|
|
||||||
template_name="services/services_overview.html",
|
|
||||||
context=context,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@services_view(
|
|
||||||
paths="<str:service_slug>/",
|
|
||||||
name="detail",
|
|
||||||
login_required=True,
|
|
||||||
)
|
|
||||||
def service_detail(request, service_slug):
|
|
||||||
service = ServiceRegistry.get(slug=service_slug)
|
|
||||||
|
|
||||||
context = {
|
|
||||||
"service": service,
|
|
||||||
}
|
|
||||||
|
|
||||||
return render(
|
|
||||||
request=request,
|
|
||||||
template_name="services/service_detail.html",
|
|
||||||
context=context,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@services_view(
|
|
||||||
paths="<str:service_slug>/subscribe/",
|
|
||||||
name="subscribe",
|
|
||||||
login_required=True,
|
|
||||||
)
|
|
||||||
def service_subscribe(request, service_slug):
|
|
||||||
service = ServiceRegistry.get(slug=service_slug)
|
|
||||||
|
|
||||||
# TODO: add a form to subscribe to the service
|
|
||||||
context = {
|
|
||||||
"service": service,
|
|
||||||
"base_path": "services:list",
|
|
||||||
"form": service.get_form(),
|
|
||||||
}
|
|
||||||
|
|
||||||
return render(
|
|
||||||
request=request,
|
|
||||||
template_name="services/service_subscribe.html",
|
|
||||||
context=context,
|
|
||||||
)
|
|
||||||
|
|
Loading…
Reference in a new issue