musician webapp visualization and firsts edits

This commit is contained in:
Jorge Pastor 2024-04-23 21:06:48 +02:00
parent bb4fc5e267
commit 3c3e15d1c5
9 changed files with 326 additions and 2 deletions

View file

@ -9,6 +9,7 @@ from orchestra.contrib.domains.models import Domain, Record
from orchestra.contrib.mailboxes.models import Address, Mailbox from orchestra.contrib.mailboxes.models import Address, Mailbox
from orchestra.contrib.systemusers.models import WebappUsers, SystemUser from orchestra.contrib.systemusers.models import WebappUsers, SystemUser
from orchestra.contrib.musician.validators import ValidateZoneMixin from orchestra.contrib.musician.validators import ValidateZoneMixin
from orchestra.contrib.webapps.models import WebApp, WebAppOption
from . import api from . import api
@ -201,3 +202,20 @@ class SystemUsersChangePasswordForm(ChangePasswordForm):
class Meta: class Meta:
fields = ("password",) fields = ("password",)
model = SystemUser model = SystemUser
class WebappOptionCreateForm(forms.ModelForm):
class Meta:
model = WebAppOption
fields = ("name", "value")
def __init__(self, *args, **kwargs):
self.webapp = kwargs.pop('webapp')
super().__init__(*args, **kwargs)
def save(self, commit=True):
instance = super().save(commit=False)
instance.webapp = self.webapp
if commit:
super().save(commit=True)
return instance

View file

@ -0,0 +1,42 @@
{% extends "musician/base.html" %}
{% load i18n %}
{% block content %}
<a class="btn-arrow-left" href="{% url 'musician:webapp-list' %}">{% trans "Go back" %}</a>
<h1 class="service-name">
{% trans "PHP settings for" %} <span class="font-weight-light">{{ object.name }}</span>
</h1>
<p class="service-description">{% trans "PHP settings page description." %}</p>
<table class="table service-list">
<colgroup>
<col span="1" style="width: 12%;">
<col span="1" style="width: 10%;">
<col span="1" style="width: 78%;">
</colgroup>
<thead class="thead-dark">
<tr>
<th scope="col">{% trans "Type" %}</th>
<th scope="col">{% trans "Value" %}</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for option in object.options.all %}
<tr>
<td>{{ option.name }}</td>
<td>{{ option.value }}</td>
<td class="text-right">
<a href="{% url 'musician:webapp-delete-option' object.pk option.pk %}">
<i class="ml-3 text-danger fas fa-trash"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<a class="btn btn-primary mt-4 mb-4" href="{% url 'musician:webapp-add-option' object.pk %}">{% trans "Add new option" %}</a></td>
{% endblock %}

View file

@ -0,0 +1,20 @@
{% extends "musician/base.html" %}
{% load bootstrap4 i18n %}
{% block content %}
<h1 class="service-name">{{ service.verbose_name }}</h1>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<a class="btn btn-light mr-2" href="{% url 'musician:address-list' %}">{% trans "Cancel" %}</a>
<button type="submit" class="btn btn-secondary">{% trans "Save" %}</button>
{% if form.instance.pk %}
<div class="float-right">
<a class="btn btn-danger" href="{% url 'musician:address-delete' view.kwargs.pk %}">{% trans "Delete" %}</a>
</div>
{% endif %}
{% endbuttons %}
</form>
{% endblock %}

View file

@ -0,0 +1,57 @@
{% extends "musician/base.html" %}
{% load bootstrap4 i18n %}
{% block content %}
<p>
Una Webapp es el diectorio donde se almacena su web,
mediante SFTP podras acceder a este directorio y subir/editar/eliminar los archivos
</p>
<p>
Cada Webapp tiene su propio usuario SFTP, este se creara automaticamente al crear la Webapp
</p>
<table class="table service-list">
<colgroup>
<col span="1" style="width: 19%;">
<col span="1" style="width: 19%;">
<col span="1" style="width: 19%;">
<col span="1" style="width: 19%;">
<col span="1" style="width: 19%;">
<col span="1" style="width: 5%;">
</colgroup>
<thead class="thead-dark">
<tr>
<th scope="col">{% trans "Name" %}</th>
<th scope="col">{% trans "Type" %}</th>
<th scope="col">{% trans "Version" %}</th>
<th scope="col">{% trans "SFTP User" %}</th>
<th scope="col">{% trans "Server" %}</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for webapp in object_list %}
<tr>
<td>{{ webapp.name }}</td>
<td>{{ webapp.type }}</td>
<td>{{ webapp.type_instance.get_detail }}</td>
<td>
{% if webapp.sftpuser %}
<a href="{% url 'musician:webappuser-list'%}">{{ webapp.sftpuser }}</a>
{% else %}
<a href="{% url 'musician:systemuser-list'%}">{{ webapp.account.main_systemuser }}</a>
{% endif %}
</td>
<td>{{ webapp.target_server }}</td>
<td>
<a class="btn btn-outline-warning" href="{% url 'musician:webapp-detail' webapp.id %}">
<i class="fas fa-tools"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
{% include "musician/components/table_paginator.html" %}
</table>
{% endblock %}

View file

@ -0,0 +1,20 @@
{% extends "musician/base.html" %}
{% load bootstrap4 i18n %}
{% block content %}
<a class="btn-arrow-left" href="{% url 'musician:webapp-detail' view.kwargs.pk %}">{% trans "Go back" %}</a>
<h1 class="service-name">
{% if form.instance.pk %}{% trans "Update record of" %}{% else %}{% trans "Add record to" %}{% endif %}
<span class="font-weight-light">{{ form.webapp.name }}</span>
</h1>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<a class="btn btn-light mr-2" href="{% url 'musician:webapp-detail' view.kwargs.pk %}">{% trans "Cancel" %}</a>
<button type="submit" class="btn btn-secondary">{% trans "Save" %}</button>
{% endbuttons %}
</form>
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends "musician/base.html" %}
{% load i18n %}
{% block content %}
<form method="post">
{% csrf_token %}
<p>{% blocktrans %}Are you sure that you want remove the following option"?{% endblocktrans %}</p>
<pre>{{ object.name}} {{ object.value}}</pre>
<p class="alert alert-warning"><strong>{% trans 'WARNING: This action cannot be undone.' %}</strong></p>
<input class="btn btn-danger" type="submit" value="{% trans 'Delete' %}">
<a class="btn btn-secondary" href="{% url 'musician:webapp-detail' view.kwargs.pk %}">{% trans 'Cancel' %}</a>
</form>
{% endblock %}

View file

@ -0,0 +1,103 @@
{% extends "musician/base.html" %}
{% load bootstrap4 i18n %}
{% block content %}
<table class="table service-list table-hover">
<colgroup>
<col span="1" style="width: 15%;">
<col span="1" style="width: 25%;">
<col span="1" style="width: 40%;">
<col span="1" style="width: 10%;">
<col span="1" style="width: 10%;">
</colgroup>
<thead class="thead-dark">
<tr>
<th scope="col">{% trans "Name" %}</th>
<th scope="col">{% trans "Url" %}</th>
<th scope="col">{% trans "Server" %}</th>
<th scope="col">{% trans "Is active?" %}</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
{% for website in object_list %}
<tr class="fila-principal" data-toggle="collapse" data-target=".detalles{{ website.id }}">
<td>{{ website.name }}</td>
<td>
{% for domain in website.domains.all %}
<a href="{{ website.get_protocol }}://{{ domain }}">{{ website.get_protocol }}://{{ domain }}</a><br>
{% endfor %}
</td>
<td>{{ website.target_server }}</td>
<td class="text-{{website.is_active|yesno:'success,danger'}}">
<i class="fa fa-{{ website.is_active|yesno:'check,times' }}"></i>
<span class="sr-only">{{ website.is_active|yesno }}</span>
</td>
<td>
<a class="btn btn-outline-warning" href="#">
<i class="fas fa-tools"></i></a>
</td>
</tr>
<!-- Fila oculta de webapp -->
<tr class="collapse detalles{{ website.id }}">
<td colspan="12"class="p-5">
<table class="table">
<tbody>
{% for content in website.content_set.all %}
<tr class="table-active">
<td>Webapp Dir</td>
<td>/home/{{ content.webapp.account }}/webapps/{{ content.webapp }}</td>
<td class="text-right">
<a class="btn btn-outline-secondary" href="#">
<i class="fas fa-tools"></i></a>
</td>
</tr>
<tr>
<td>Url</td>
{% if website.domains.first %}
<td><a href="{{ website.get_protocol }}://{{ website.domains.first }}{{ content.path }}">
{{ website.get_protocol }}://{{ website.domains.first }}{{ content.path }}</a>
</td>
{% else %}
<td></td>
{% endif %}
<td></td>
</tr>
<tr>
<td>Type</td>
{% if content.webapp.type == "php" %}
<td>PHP {{ content.webapp.type_instance.get_detail }}</td>
{% else %}
<td>{{ content.webapp.type }}</td>
{% endif %}
<td></td>
</tr>
<tr>
{% if content.webapp.sftpuser %}
<td>SFTP user</td>
<td>{{ content.webapp.sftpuser }}</td>
<td class="text-right">
<a class="btn btn-outline-warning" href="{% url 'musician:webappuser-password' content.webapp.sftpuser.id %}">
<i class="fas fa-key"></i> {% trans "Update password" %}</a>
</td>
{% else %}
<td>FTP user</td>
<td>{{ content.webapp.account.main_systemuser }}</td>
<td class="text-right">
<a class="btn btn-outline-warning" href="{% url 'musician:systemuser-password' content.webapp.account.main_systemuser.id %}">
<i class="fas fa-key"></i> {% trans "Update password" %}</a>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
</td>
</tr>
{% endfor %}
</tbody>
{% include "musician/components/table_paginator.html" %}
</table>
{% endblock %}

View file

@ -45,4 +45,9 @@ urlpatterns = [
path('systemusers/', views.SystemUserListView.as_view(), name='systemuser-list'), path('systemusers/', views.SystemUserListView.as_view(), name='systemuser-list'),
path('systemuser/<int:pk>/change-password/', views.SystemUserChangePasswordView.as_view(), name='systemuser-password'), path('systemuser/<int:pk>/change-password/', views.SystemUserChangePasswordView.as_view(), name='systemuser-password'),
path('websites/', views.WebsiteListView.as_view(), name='website-list'), path('websites/', views.WebsiteListView.as_view(), name='website-list'),
path('webapps/', views.WebappListView.as_view(), name='webapp-list'),
path('webapps/<int:pk>/', views.WebappDetailView.as_view(), name='webapp-detail'),
path('webapps/<int:pk>/add-option/', views.WebappAddOptionView.as_view(), name='webapp-add-option'),
path('webapps/<int:pk>/option/<int:option_pk>/delete/', views.WebappDeleteOptionView.as_view(), name='webapp-delete-option'),
] ]

View file

@ -34,13 +34,14 @@ from orchestra.contrib.resources.models import Resource, ResourceData
from orchestra.contrib.saas.models import SaaS from orchestra.contrib.saas.models import SaaS
from orchestra.contrib.systemusers.models import WebappUsers, SystemUser from orchestra.contrib.systemusers.models import WebappUsers, SystemUser
from orchestra.contrib.websites.models import Website from orchestra.contrib.websites.models import Website
from orchestra.contrib.webapps.models import WebApp, WebAppOption
from orchestra.utils.html import html_to_pdf from orchestra.utils.html import html_to_pdf
from .auth import logout as auth_logout from .auth import logout as auth_logout
from .forms import (LoginForm, MailboxChangePasswordForm, MailboxCreateForm, from .forms import (LoginForm, MailboxChangePasswordForm, MailboxCreateForm,
MailboxSearchForm, MailboxUpdateForm, MailForm, MailboxSearchForm, MailboxUpdateForm, MailForm,
RecordCreateForm, RecordUpdateForm, WebappUsersChangePasswordForm, RecordCreateForm, RecordUpdateForm, WebappUsersChangePasswordForm,
SystemUsersChangePasswordForm) SystemUsersChangePasswordForm, WebappOptionCreateForm)
from .mixins import (CustomContextMixin, ExtendedPaginationMixin, from .mixins import (CustomContextMixin, ExtendedPaginationMixin,
UserTokenRequiredMixin) UserTokenRequiredMixin)
from .models import Address as AddressService from .models import Address as AddressService
@ -659,3 +660,48 @@ class WebsiteListView(ServiceListView):
# Translators: This message appears on the page title # Translators: This message appears on the page title
'title': _('Websites'), 'title': _('Websites'),
} }
class WebappListView(ServiceListView):
model = WebApp
template_name = "musician/webapp_list.html"
extra_context = {
# Translators: This message appears on the page title
'title': _('Webapps'),
}
class WebappDetailView(CustomContextMixin, UserTokenRequiredMixin, DetailView):
template_name = "musician/webapp_detail.html"
extra_context = {
# Translators: This message appears on the page title
'title': _('webapp details'),
}
def get_queryset(self):
return WebApp.objects.filter(account=self.request.user)
class WebappAddOptionView(CustomContextMixin, UserTokenRequiredMixin, CreateView):
model = WebAppOption
form_class = WebappOptionCreateForm
template_name = "musician/webapp_option_form.html"
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
webapp = get_object_or_404(WebApp, account=self.request.user, pk=self.kwargs["pk"])
kwargs['webapp'] = webapp
return kwargs
def get_success_url(self):
return reverse_lazy("musician:webapp-detail", kwargs={"pk": self.kwargs["pk"]})
class WebappDeleteOptionView(CustomContextMixin, UserTokenRequiredMixin, DeleteView):
model = WebAppOption
template_name = "musician/webappoption_check_delete.html"
pk_url_kwarg = "option_pk"
def get_queryset(self):
qs = WebAppOption.objects.filter(webapp__account=self.request.user, webapp=self.kwargs["pk"])
return qs
def get_success_url(self):
return reverse_lazy("musician:webapp-detail", kwargs={"pk": self.kwargs["pk"]})