Schema{{ organization.public_schemas.count|pluralize }}
+-
+ {% for schema in organization.public_schemas.all %}
+
- + + {{ schema.name }} + + + {% endfor %} +
This organization has no schemas.
+ {% endif %} +diff --git a/core/admin.py b/core/admin.py index 457fdca..ad3176f 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,6 +1,8 @@ from django.contrib import admin from django.contrib.admin.decorators import register -from .models import Schema, SchemaRef, DocumentationItem +from .models import ( + Schema, SchemaRef, DocumentationItem, Organization +) @register(Schema) @@ -17,3 +19,7 @@ class SchemaRefAdmin(admin.ModelAdmin): class DocumentationItemAdmin(admin.ModelAdmin): list_display = ['schema', 'name', 'url'] + +@register(Organization) +class OrganizationAdmin(admin.ModelAdmin): + list_display = ['name', 'url_path_segment', 'primary_account'] diff --git a/core/migrations/0009_organization.py b/core/migrations/0009_organization.py new file mode 100644 index 0000000..4baf81f --- /dev/null +++ b/core/migrations/0009_organization.py @@ -0,0 +1,31 @@ +# Generated by Django 5.2.5 on 2026-01-19 18:26 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0008_alter_documentationitem_format_and_more'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Organization', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=200)), + ('url_path_segment', models.CharField(max_length=100, unique=True)), + ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to=settings.AUTH_USER_MODEL)), + ('primary_account', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, related_name='primary_account_of', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/core/models.py b/core/models.py index cd6e605..ecac09b 100644 --- a/core/models.py +++ b/core/models.py @@ -58,6 +58,16 @@ def url_providers(self): } return provider_names + @property + def organization(self): + # TODO: In theory a User could be the primary_account_of multiple Organizations, + # in which case we won't know which of that Users' schemas + # belong to which Organizations. + # For now, we'll assume users (as in "person using Schemas.Pub") + # only manage one organization per User (model) + # and list all their schemas under that one organization. + return Organization.objects.filter(primary_account=self.created_by).first() + def _latest_documentation_item_of_type(self, role): return self.documentationitem_set.filter(role=role).order_by('-created_at').first() @@ -207,6 +217,7 @@ def repo_url(self): normal_path = "/".join([user, repo, "blob", branch] + filepath) return f"https://{self.REPO_NETLOC}/{normal_path}" + class ReferenceItem(BaseModel): class Meta: abstract = True @@ -272,3 +283,16 @@ def __str__(self): def language(self): return guess_language_by_extension(self.url, ['markdown']) + +class Organization(BaseModel): + name = models.CharField(max_length=200) + primary_account = models.ForeignKey(User, on_delete=models.RESTRICT, related_name="primary_account_of") + url_path_segment = models.CharField(max_length=100, unique=True) + + def __str__(self): + return self.name + + @property + def public_schemas(self): + return Schema.public_objects.filter(created_by=self.primary_account) + diff --git a/core/static/css/site.css b/core/static/css/site.css index 7ef790c..0389aae 100644 --- a/core/static/css/site.css +++ b/core/static/css/site.css @@ -904,3 +904,12 @@ a.badge--w3c { padding: 1rem; gap: 1rem; } + +/* Organization detail */ +.organization-detail { + display: flex; + flex-direction: column; + align-items: center; + padding: 1rem; + gap: 1rem; +} diff --git a/core/templates/account/profile.html b/core/templates/account/profile.html index 15da98b..c7776a1 100644 --- a/core/templates/account/profile.html +++ b/core/templates/account/profile.html @@ -44,6 +44,20 @@
This organization has no schemas.
+ {% endif %} +