Merge pull request 'Show CNAME information for Control Planes' (#179) from 157-cname-information into main
Reviewed-on: #179
This commit is contained in:
commit
5f75b0465f
8 changed files with 167 additions and 50 deletions
|
|
@ -191,6 +191,15 @@ class ControlPlaneAdmin(admin.ModelAdmin):
|
|||
),
|
||||
},
|
||||
),
|
||||
(
|
||||
_("User Information"),
|
||||
{
|
||||
"fields": ("user_info",),
|
||||
"description": _(
|
||||
"Key-value information displayed to users when selecting this control plane."
|
||||
),
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
def get_exclude(self, request, obj=None):
|
||||
|
|
|
|||
|
|
@ -1,8 +1,20 @@
|
|||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_jsonform.widgets import JSONFormWidget
|
||||
|
||||
from servala.core.models import ControlPlane, ServiceDefinition
|
||||
|
||||
CONTROL_PLANE_USER_INFO_SCHEMA = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"CNAME Record": {
|
||||
"title": "CNAME Record",
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
"additionalProperties": {"type": "string"},
|
||||
}
|
||||
|
||||
|
||||
class ControlPlaneAdminForm(forms.ModelForm):
|
||||
certificate_authority_data = forms.CharField(
|
||||
|
|
@ -23,6 +35,7 @@ class ControlPlaneAdminForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = ControlPlane
|
||||
fields = "__all__"
|
||||
widgets = {"user_info": JSONFormWidget(schema=CONTROL_PLANE_USER_INFO_SCHEMA)}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
# Generated by Django 5.2.6 on 2025-09-08 07:14
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("core", "0006_remove_service_instance_soft_delete"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="controlplane",
|
||||
name="user_info",
|
||||
field=models.JSONField(
|
||||
blank=True,
|
||||
help_text="Key-value information displayed to users when selecting this control plane",
|
||||
null=True,
|
||||
verbose_name="User Information",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="cloudprovider",
|
||||
name="external_links",
|
||||
field=models.JSONField(
|
||||
blank=True,
|
||||
help_text='JSON array of link objects: {"url": "…", "title": "…"}. ',
|
||||
null=True,
|
||||
verbose_name="External links",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="service",
|
||||
name="external_links",
|
||||
field=models.JSONField(
|
||||
blank=True,
|
||||
help_text='JSON array of link objects: {"url": "…", "title": "…", "featured": false}. Featured links will be shown on the service list page, all other links will only show on the service and offering detail pages.',
|
||||
null=True,
|
||||
verbose_name="External links",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
@ -144,6 +144,14 @@ class ControlPlane(ServalaModelMixin, models.Model):
|
|||
related_name="control_planes",
|
||||
verbose_name=_("Cloud provider"),
|
||||
)
|
||||
user_info = models.JSONField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name=_("User Information"),
|
||||
help_text=_(
|
||||
"Key-value information displayed to users when selecting this control plane"
|
||||
),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Control plane")
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if instance.status_conditions %}
|
||||
<div class="col-12 col-md-7">
|
||||
<div class="col-12 col-md-7">
|
||||
{% if instance.status_conditions %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4>{% translate "Status" %}</h4>
|
||||
|
|
@ -101,8 +101,9 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% include "includes/control_plane_user_info.html" with control_plane=instance.context.control_plane %}
|
||||
</div>
|
||||
{% if instance.spec and spec_fieldsets %}
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
|
|
|
|||
|
|
@ -7,6 +7,11 @@
|
|||
{{ offering }}
|
||||
{% endblock page_title %}
|
||||
{% endblock html_title %}
|
||||
{% partialdef control-plane-info %}
|
||||
{% if selected_plane %}
|
||||
{% include "includes/control_plane_user_info.html" with control_plane=selected_plane %}
|
||||
{% endif %}
|
||||
{% endpartialdef %}
|
||||
{% partialdef service-form %}
|
||||
{% if service_form %}
|
||||
<div class="card">
|
||||
|
|
@ -25,57 +30,69 @@
|
|||
{% endpartialdef %}
|
||||
{% block content %}
|
||||
<section class="section">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex align-items-center">
|
||||
{% if service.logo %}
|
||||
<img src="{{ service.logo.url }}"
|
||||
alt="{{ service.name }}"
|
||||
class="me-3"
|
||||
style="max-width: 48px;
|
||||
max-height: 48px">
|
||||
{% endif %}
|
||||
<div class="d-flex flex-column">
|
||||
<h4 class="mb-0">{{ offering }}</h4>
|
||||
<small class="text-muted">{{ offering.service.category }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if offering.description %}
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<p>{{ offering.description|urlize }}</p>
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header d-flex align-items-center">
|
||||
{% if service.logo %}
|
||||
<img src="{{ service.logo.url }}"
|
||||
alt="{{ service.name }}"
|
||||
class="me-3"
|
||||
style="max-width: 48px;
|
||||
max-height: 48px">
|
||||
{% endif %}
|
||||
<div class="d-flex flex-column">
|
||||
<h4 class="mb-0">{{ offering }}</h4>
|
||||
<small class="text-muted">{{ offering.service.category }}</small>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not has_control_planes %}
|
||||
<p>{% translate "We currently cannot offer this service, sorry!" %}</p>
|
||||
{% else %}
|
||||
<form hx-trigger="change"
|
||||
hx-get="{{ request.path }}?fragment=service-form"
|
||||
hx-target="#service-form">
|
||||
{{ select_form }}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if service.external_links %}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<h6 class="mb-3">{% translate "External Links" %}</h6>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
{% for link in service.external_links %}
|
||||
<a href="{{ link.url }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn btn-outline-primary btn-sm">
|
||||
{{ link.title }}
|
||||
<i class="bi bi-box-arrow-up-right ms-1"></i>
|
||||
</a>
|
||||
{% endfor %}
|
||||
<div class="card-body">
|
||||
{% if offering.description %}
|
||||
<div class="row mb-3">
|
||||
<div class="col-12">
|
||||
<p>{{ offering.description|urlize }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not has_control_planes %}
|
||||
<p>{% translate "We currently cannot offer this service, sorry!" %}</p>
|
||||
{% else %}
|
||||
<form hx-trigger="change"
|
||||
hx-get="{{ request.path }}?fragment=service-form"
|
||||
hx-target="#service-form"
|
||||
hx-swap="outerHTML">
|
||||
{{ select_form }}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if service.external_links %}
|
||||
<div class="row mt-3">
|
||||
<div class="col-12">
|
||||
<h6 class="mb-3">{% translate "External Links" %}</h6>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
{% for link in service.external_links %}
|
||||
<a href="{{ link.url }}"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="btn btn-outline-primary btn-sm">
|
||||
{{ link.title }}
|
||||
<i class="bi bi-box-arrow-up-right ms-1"></i>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="service-form">{% partial service-form %}</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-4">
|
||||
{% if has_control_planes %}
|
||||
<div id="control-plane-info"
|
||||
hx-trigger="load, change from:form"
|
||||
hx-get="{{ request.path }}?fragment=control-plane-info">{% partial control-plane-info %}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id="service-form">{% partial service-form %}</div>
|
||||
</section>
|
||||
{% endblock content %}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
{% load i18n %}
|
||||
{% comment %}
|
||||
Reusable snippet for displaying ControlPlane user_info
|
||||
Usage: {% include "includes/control_plane_user_info.html" with control_plane=control_plane_object %}
|
||||
{% endcomment %}
|
||||
{% if control_plane.user_info %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">{% translate "Service Provider Zone Information" %}</h4>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<div class="table-responsive">
|
||||
<table class="table mb-0 table-lg">
|
||||
<tbody>
|
||||
{% for key, value in control_plane.user_info.items %}
|
||||
<tr>
|
||||
<th>{{ key }}</th>
|
||||
<td>{{ value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
@ -73,7 +73,7 @@ class ServiceOfferingDetailView(OrganizationViewMixin, HtmxViewMixin, DetailView
|
|||
context_object_name = "offering"
|
||||
model = ServiceOffering
|
||||
permission_type = "view"
|
||||
fragments = ("service-form",)
|
||||
fragments = ("service-form", "control-plane-info")
|
||||
|
||||
def has_permission(self):
|
||||
return self.has_organization_permission()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue