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>
<a href="{% url 'service_list' %}">Services</a>
</li>
<li>
<a href="{% url 'configuration_form' %}">Configuration</a>
</li>
{% load custom_tags %}
<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 %}
<h1>Fediversity Panel</h1>
<p>Hello world!</p>
{% endblock %}

View file

@ -25,4 +25,5 @@ urlpatterns = [
path("", include("django.contrib.auth.urls")),
path("account/", views.AccountDetail.as_view(), name='account_detail'),
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.views.generic import TemplateView
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):
template_name = 'index.html'
class AccountDetail(LoginRequiredMixin, DetailView):
model = User
template_name = 'account_detail.html'
def get_object(self):
return self.request.user
class ServiceList(TemplateView):
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