1
0
Fork 0

Compare commits

...
Sign in to create a new pull request.

23 commits

Author SHA1 Message Date
1076552f75
try a few ways to make form load/submit work, add stub on inline formsets to display nested forms
c.f.:

-
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets
- https://swapps.com/blog/working-with-nested-forms-with-django/
2025-03-04 10:36:15 +01:00
4273c7a608
add model changes to make it boot (allow null) 2025-03-04 09:09:50 +01:00
a088d25eb7
adjust obsolete naming 2025-03-03 17:00:16 +01:00
b3b525dee4
clean-up 2025-03-03 15:58:37 +01:00
8fabd7f0f1
reverse relationship 2025-03-03 15:51:19 +01:00
0f2c5390d6
strip out admin stuff for now 2025-03-03 15:45:39 +01:00
fb5e8ae453
rename checkboxes to fix nix model 2025-03-03 15:31:27 +01:00
014c3efc70
expand to multiple lines to allow commenting parts 2025-03-03 15:29:25 +01:00
1ebea118cc
migration: rm replaces 2025-03-03 14:26:11 +01:00
99b8fe8870
Squash migrations 2025-03-03 12:28:31 +01:00
d32e11389c
Remove trailing whitespace 2025-03-03 12:28:31 +01:00
624e354da5
fix saving for sepparated models 2025-03-03 12:28:31 +01:00
7c8c6c7eb9
Seperation of configuration form 2025-03-03 12:28:31 +01:00
da2e7d93a9
add enable toggle, let operators have many configuraitons
this commit is a bit of a jumble, but it allows us to disable
a configuration so the associated deployment can in principle be
garbage-collected, and allows operators to have multiple configurations.
it also (as a temporary hack) ties the deployment subdomain to the username so it's clear to
operators where we're deploying to.
2025-03-03 12:28:31 +01:00
a97418c30e
rename form and add navigation element 2025-03-03 12:28:31 +01:00
af1c051498
don't use hungarian notation for model names 2025-03-03 12:28:31 +01:00
c8062a87d4
fix wrong rebase of base.html 2025-03-03 12:28:31 +01:00
2fb0ccf0aa
add redirection to deploy page after save 2025-03-03 12:28:31 +01:00
46107cb21d
Add domain dropdown menu 2025-03-03 12:28:31 +01:00
17570c6434
add first part of save able login 2025-03-03 12:28:31 +01:00
bb12139f5a
Add saveable Deploy services form 2025-03-03 12:28:31 +01:00
66e88325ec
add first part of save able login 2025-03-03 12:28:31 +01:00
Kiara Grouwstra
4c1aa34d97
add simple form, closes ,
note that i haven't done much with validation or conditional
enabling/hiding - if we start doing front-end frameworks those may have
opinions on this
2025-03-03 12:28:31 +01:00
10 changed files with 270 additions and 1 deletions

1
panel/src/panel/admin.py Normal file
View file

@ -0,0 +1 @@

47
panel/src/panel/forms.py Normal file
View file

@ -0,0 +1,47 @@
from django import forms
# from django.forms import inlineformset_factory
from panel.models import (
Configuration,
PeertubeConfig,
PixelfedConfig,
MastodonConfig,
)
class Deployment(forms.ModelForm):
class Meta:
model = Configuration
fields = [
'domain',
]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
operator = self.instance.operator.username
self.fields['domain'].choices = [
(code, f"{operator}.{label}")
for code, label in self.instance._meta.get_field('domain').choices
]
model = Configuration
fields = ['domain']
class MastodonConfigForm(forms.ModelForm):
class Meta:
model = MastodonConfig
fields = ['enable']
class PixelfedConfigForm(forms.ModelForm):
class Meta:
model = PixelfedConfig
fields = ['enable']
class PeertubeConfigForm(forms.ModelForm):
class Meta:
model = PeertubeConfig
fields = ['enable']
# BookInlineFormSet = inlineformset_factory(Deployment, MastodonConfigForm, extra=1, can_delete=False)

View file

@ -0,0 +1,50 @@
# Generated by Django 4.2.16 on 2025-03-04 07:55
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='MastodonConfig',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enable', models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='PeertubeConfig',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enable', models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='PixelfedConfig',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enable', models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='Configuration',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enable', models.BooleanField(default=False, help_text='Enable the configuration')),
('domain', models.CharField(choices=[('fediversity_eu', 'fediversity.eu'), ('fediversity_net', 'fediversity.net')], max_length=255)),
('mastodon', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='config', to='panel.mastodonconfig')),
('operator', models.ForeignKey(help_text='Operator who owns the configuration', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='configurations', to=settings.AUTH_USER_MODEL)),
('peertube', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='config', to='panel.peertubeconfig')),
('pixelfed', models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='config', to='panel.pixelfedconfig')),
],
),
]

View file

95
panel/src/panel/models.py Normal file
View file

@ -0,0 +1,95 @@
from django.db import models
from django.contrib.auth.models import User
class MastodonConfig(models.Model):
enable = models.BooleanField(default=False)
def __str__(self):
return f"Mastodon: {self.enable}"
class PixelfedConfig(models.Model):
enable = models.BooleanField(default=False)
def __str__(self):
return f"Pixelfed: {self.enable}"
class PeertubeConfig(models.Model):
enable = models.BooleanField(default=False)
def __str__(self):
return f"Peertube: {self.enable}"
class Configuration(models.Model):
class ConfigurationManager(models.Manager):
# Define which related fields should be auto-created
auto_create_related = [
'mastodon',
'pixelfed',
'peertube',
]
def create(self, **kwargs):
# Create the model instance but don't save yet
instance = self.model(**kwargs)
# Auto-create any missing related objects
for field_name in self.auto_create_related:
# Skip if the field was provided
if field_name in kwargs:
continue
# Get the related model class
field = self.model._meta.get_field(field_name)
related_model = field.related_model
# Create the related object
related_obj = related_model.objects.create()
# Set the relation
setattr(instance, field_name, related_obj)
# Save and return the instance
instance.save()
return instance
operator = models.ForeignKey(
User,
on_delete=models.SET_NULL,
null=True,
related_name="configurations",
help_text="Operator who owns the configuration",
)
enable = models.BooleanField(
default=False,
help_text="Enable the configuration",
)
domain = models.CharField(
# XXX: hard-code available apex domains for now,
# they will be prefixed by the user name
# TODO: map to user's registered domains
choices=[
("fediversity_eu", "fediversity.eu"),
("fediversity_net", "fediversity.net")
],
max_length=255,
)
mastodon = models.OneToOneField(MastodonConfig, on_delete=models.CASCADE, related_name='config', null=True)
pixelfed = models.OneToOneField(PixelfedConfig, on_delete=models.CASCADE, related_name='config', null=True)
peertube = models.OneToOneField(PeertubeConfig, on_delete=models.CASCADE, related_name='config', null=True)
# Use the custom manager
# objects = ConfigurationManager()
# def save(self, *args, **kwargs):
# # Create the related items if they don't exist
# if not hasattr(self, 'mastodon'):
# MastodonConfig.objects.create(config=self)
# if not hasattr(self, 'mastodon'):
# MastodonConfig.objects.create(config=self)
# if not hasattr(self, 'mastodon'):
# MastodonConfig.objects.create(config=self)
# super().save(*args, **kwargs)

View file

@ -26,6 +26,9 @@
<li> <li>
<a href="{% url 'service_list' %}">Services</a> <a href="{% url 'service_list' %}">Services</a>
</li> </li>
<li>
<a href="{% url 'configuration_form' %}">Configuration</a>
</li>
{% load custom_tags %} {% load custom_tags %}
<li> <li>

View file

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block content %}
<form method="post" enctype="multipart/form-data" action="{% url 'configuration_form' %}">
{% csrf_token %}
{{ form.as_p }}
<h2>Services</h2>
{{ children.as_p }}
<button class="button" disabled>Deploy</button>
<button class="button" type="submit" >Save</button>
</form>
{% endblock %}

View file

@ -3,5 +3,4 @@
{% block content %} {% block content %}
<h1>Fediversity Panel</h1> <h1>Fediversity Panel</h1>
<p>Hello world!</p>
{% endblock %} {% endblock %}

View file

@ -25,4 +25,5 @@ urlpatterns = [
path("", include("django.contrib.auth.urls")), path("", include("django.contrib.auth.urls")),
path("account/", views.AccountDetail.as_view(), name='account_detail'), path("account/", views.AccountDetail.as_view(), name='account_detail'),
path("services/", views.ServiceList.as_view(), name='service_list'), path("services/", views.ServiceList.as_view(), name='service_list'),
path("configuration/", views.ConfigurationForm.as_view(), name='configuration_form'),
] ]

View file

@ -2,15 +2,75 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.views.generic import TemplateView from django.views.generic import TemplateView
from django.views.generic import DetailView from django.views.generic import DetailView
from django.views.generic.edit import UpdateView
from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy
from panel.models import Configuration
from panel import forms
from .models import (
Configuration,
MastodonConfig,
PixelfedConfig,
PeertubeConfig,
)
from .forms import (
Deployment,
MastodonConfigForm,
PixelfedConfigForm,
PeertubeConfigForm,
)
class Index(TemplateView): class Index(TemplateView):
template_name = 'index.html' template_name = 'index.html'
class AccountDetail(LoginRequiredMixin, DetailView): class AccountDetail(LoginRequiredMixin, DetailView):
model = User model = User
template_name = 'account_detail.html' template_name = 'account_detail.html'
def get_object(self): def get_object(self):
return self.request.user return self.request.user
class ServiceList(TemplateView): class ServiceList(TemplateView):
template_name = 'service_list.html' template_name = 'service_list.html'
class ConfigurationForm(LoginRequiredMixin, UpdateView):
template_name = 'configuration_form.html'
model = Configuration
form_class = forms.Deployment
success_url = reverse_lazy('configuration_form')
def get_object(self, queryset=None):
obj, created = Configuration.objects.get_or_create(
operator=self.request.user)
return obj
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
deploy_form = self.get_object()
context.update({
'mastodon_form': MastodonConfigForm(self.request.POST or None, instance=deploy_form.mastodon),
'pixelfed_form': PixelfedConfigForm(self.request.POST or None, instance=deploy_form.pixelfed),
'peertube_form': PeertubeConfigForm(self.request.POST or None, instance=deploy_form.peertube),
})
return context
def form_valid(self, form):
response = super().form_valid(form) # Save main Configuration form
obj = self.get_object() # Get instance
# Save related forms
MastodonConfigForm(self.request.POST,
instance=obj.mastodon).save()
PixelfedConfigForm(self.request.POST,
instance=obj.pixelfed).save()
PeertubeConfigForm(self.request.POST,
instance=obj.peertube).save()
return response