Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion core/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
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,
Profile
)


@register(Schema)
Expand All @@ -17,3 +23,12 @@ class SchemaRefAdmin(admin.ModelAdmin):
class DocumentationItemAdmin(admin.ModelAdmin):
list_display = ['schema', 'name', 'url']


@register(Organization)
class OrganizationAdmin(admin.ModelAdmin):
list_display = ['name', 'slug']


@register(Profile)
class ProfileAdmin(admin.ModelAdmin):
list_display = ['user', 'organization']
3 changes: 3 additions & 0 deletions core/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
class CoreConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'core'

def ready(self):
import core.signals
56 changes: 56 additions & 0 deletions core/migrations/0009_organization_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Generated by Django 5.2.5 on 2026-01-21 17:08
# Edited to create Profiles for existing users

import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models


def create_profiles(apps, schema_editor):
User = apps.get_model("auth", "User")
Profile = apps.get_model("core", "Profile")

profiles = []
for user in User.objects.all():
profiles.append(Profile(user=user))

Profile.objects.bulk_create(profiles, ignore_conflicts=True)


def remove_profiles(apps, schema_editor):
Profile = apps.get_model("core", "Profile")
Profile.objects.all().delete()


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)),
('slug', models.SlugField(unique=True)),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.RESTRICT, to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Profile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('organization', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='core.organization')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.RunPython(create_profiles, remove_profiles)
]
25 changes: 25 additions & 0 deletions core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.db.models import Q
from django.contrib.auth.models import User
from django.utils import timezone
from django.conf import settings
from urllib.parse import urlparse
import requests
from .utils import guess_specification_language_by_extension, guess_language_by_extension
Expand Down Expand Up @@ -58,6 +59,10 @@ def url_providers(self):
}
return provider_names

@property
def organization(self):
return self.created_by.profile.organization

def _latest_documentation_item_of_type(self, role):
return self.documentationitem_set.filter(role=role).order_by('-created_at').first()

Expand Down Expand Up @@ -207,6 +212,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
Expand Down Expand Up @@ -272,3 +278,22 @@ def __str__(self):
def language(self):
return guess_language_by_extension(self.url, ['markdown'])


class Organization(BaseModel):
name = models.CharField(max_length=200)
slug = models.SlugField(unique=True)

def __str__(self):
return self.name

@property
def public_schemas(self):
return Schema.public_objects.filter(
created_by_id__in=self.profile_set.values_list("user_id", flat=True)
)


class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
organization = models.ForeignKey(Organization, blank=True, null=True, on_delete=models.SET_NULL)

13 changes: 13 additions & 0 deletions core/signals.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver

from .models import Profile

User = settings.AUTH_USER_MODEL

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)

9 changes: 9 additions & 0 deletions core/static/css/site.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
12 changes: 12 additions & 0 deletions core/templates/account/profile.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ <h1>My Schemas</h1>
{% endif %}
<a href="{% url 'manage_schema_new' %}" class="button button--prominent">Add schema</a>
</section>
{% if request.user.profile.organization %}
<section class="text--secondary">
<h1>Organization</h1>
<ul>
<li>
<a href="{% url 'organization_detail' organization_id=request.user.profile.organization.id %}">
{{ request.user.profile.organization.name }}
</a>
</li>
</ul>
</section>
{% endif %}
<section class="text--secondary">
<h1>Account</h1>
Email: {{ request.user.email }}
Expand Down
26 changes: 26 additions & 0 deletions core/templates/core/organizations/detail.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{% extends "core/layouts/base.html" %}
{% block head_title %}
{{ organization.name }} | Schemas.Pub
{% endblock %}

{% block page_content %}
<main class="organization-detail">
<h1>{{ organization.name }}</h1>
<div class="main-content">
{% if organization.public_schemas.count %}
<h2>Schema{{ organization.public_schemas.count|pluralize }}</h2>
<ul>
{% for schema in organization.public_schemas.all %}
<li>
<a href="{% url 'schema_detail' schema_id=schema.id %}">
{{ schema.name }}
</a>
</li>
{% endfor %}
</ul>
{% else %}
<p>This organization has no schemas.</p>
{% endif %}
</div>
</main>
{% endblock %}
10 changes: 9 additions & 1 deletion core/templates/core/schemas/layout.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,15 @@ <h1>
<div class="header__info">
<span>Added on {{ schema.created_at | date }}</span>
&bull;
<span>Managed by {{ schema.created_by }}</span>
<span>Managed by
{% if schema.organization %}
<a href="{% url 'organization_detail' organization_id=schema.organization.id %}">
{{ schema.organization.name }}
</a>
{% else %}
{{ schema.created_by }}
{% endif %}
</span>
</div>
</div>
<nav class="schema-layout__nav">
Expand Down
3 changes: 2 additions & 1 deletion core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
path("manage/schema/<int:schema_id>", views.manage_schema, name="manage_schema"),
path("manage/schema/new", views.manage_schema, name="manage_schema_new"),
path("manage/schema/<int:schema_id>/delete", views.manage_schema_delete, name="manage_schema_delete"),
path("manage/schema/<int:schema_id>/publish", views.manage_schema_publish, name="manage_schema_publish")
path("manage/schema/<int:schema_id>/publish", views.manage_schema_publish, name="manage_schema_publish"),
path("organization/<int:organization_id>", views.organization_detail, name="organization_detail")
]

10 changes: 9 additions & 1 deletion core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
from .models import (Schema,
DocumentationItem,
SchemaRef,
DocumentationItem)
DocumentationItem,
Organization)
from .forms import SchemaForm, DocumentationItemForm


Expand Down Expand Up @@ -301,3 +302,10 @@ def manage_schema_publish(request, schema_id):

def about(request):
return render(request, "core/about.html")


def organization_detail(request, organization_id):
organization = get_object_or_404(Organization, id=organization_id)
return render(request, "core/organizations/detail.html", {
'organization': organization,
})