Compare commits
No commits in common. "8cd3605d48bffdad062d607ff28be9fa9ef7b896" and "ad11f64e52cbe408888098c17296e7223fd91c4e" have entirely different histories.
8cd3605d48
...
ad11f64e52
|
@ -29,9 +29,6 @@ class MySQLController(ServiceController):
|
|||
# Create database and re-set permissions
|
||||
mysql -e 'CREATE DATABASE `%(database)s`;' || true
|
||||
mysql mysql -e 'DELETE FROM db WHERE db = "%(database)s";'\
|
||||
|
||||
# wait to create user
|
||||
sleep 1.5
|
||||
""") % context
|
||||
)
|
||||
for user in database.users.all():
|
||||
|
|
|
@ -145,9 +145,8 @@ class Domain(models.Model):
|
|||
else:
|
||||
zone += subdomain.render_records()
|
||||
###darmengo 2021-03-25 add autoconfig
|
||||
# TODO: que se asigne esta ip automaticamente
|
||||
if self.has_default_mx():
|
||||
zone += 'autoconfig.{}. 30m IN A 45.150.186.197\n'.format(self.name)
|
||||
zone += 'autoconfig.{}. 30m IN A 109.69.8.133\n'.format(self.name)
|
||||
###END darmengo 2021-03-25 add autoconfig
|
||||
for subdomain in sorted(tail, key=lambda x: len(x.name), reverse=True):
|
||||
zone += subdomain.render_records()
|
||||
|
|
|
@ -2,6 +2,8 @@ from django import forms
|
|||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.encoding import force_str
|
||||
from orchestra.forms.widgets import DynamicHelpTextSelect
|
||||
|
||||
from django.contrib.auth.hashers import make_password
|
||||
|
||||
|
@ -9,8 +11,12 @@ from orchestra.contrib.domains.models import Domain, Record
|
|||
from orchestra.contrib.mailboxes.models import Address, Mailbox
|
||||
from orchestra.contrib.systemusers.models import WebappUsers, SystemUser
|
||||
from orchestra.contrib.musician.validators import ValidateZoneMixin
|
||||
from orchestra.contrib.webapps.models import WebApp, WebAppOption
|
||||
from orchestra.contrib.webapps.options import AppOption
|
||||
from orchestra.contrib.webapps.types import AppType
|
||||
|
||||
from . import api
|
||||
from .settings import MUSICIAN_EDIT_ENABLE_PHP_OPTIONS
|
||||
|
||||
|
||||
class LoginForm(AuthenticationForm):
|
||||
|
@ -202,3 +208,63 @@ class SystemUsersChangePasswordForm(ChangePasswordForm):
|
|||
fields = ("password",)
|
||||
model = SystemUser
|
||||
|
||||
|
||||
class WebappOptionForm(forms.ModelForm):
|
||||
|
||||
OPTIONS_HELP_TEXT = {
|
||||
op.name: force_str(op.help_text) for op in AppOption.get_plugins()
|
||||
}
|
||||
|
||||
class Meta:
|
||||
model = WebAppOption
|
||||
fields = ("name", "value")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
self.webapp = kwargs.pop('webapp')
|
||||
super().__init__(*args, **kwargs)
|
||||
except:
|
||||
super().__init__(*args, **kwargs)
|
||||
self.webapp = self.instance.webapp
|
||||
|
||||
target = 'this.id.replace("name", "value")'
|
||||
self.fields['name'].widget.attrs = DynamicHelpTextSelect(target, self.OPTIONS_HELP_TEXT).attrs
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super().save(commit=False)
|
||||
instance.webapp = self.webapp
|
||||
if commit:
|
||||
super().save(commit=True)
|
||||
self.webapp.save()
|
||||
return instance
|
||||
|
||||
|
||||
class WebappOptionCreateForm(WebappOptionForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
plugin = AppType.get(self.webapp.type)
|
||||
choices = list(plugin.get_group_options_choices())
|
||||
for grupo, opciones in enumerate(choices):
|
||||
if isinstance(opciones[1], list):
|
||||
nueva_lista = [opc for opc in opciones[1] if opc[0] in MUSICIAN_EDIT_ENABLE_PHP_OPTIONS]
|
||||
choices[grupo] = (opciones[0], nueva_lista)
|
||||
self.fields['name'].widget.choices = choices
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
name = self.cleaned_data.get("name")
|
||||
if WebAppOption.objects.filter(webapp=self.webapp, name=name).exists():
|
||||
raise ValidationError(_("This option already exist."))
|
||||
return cleaned_data
|
||||
|
||||
class WebappOptionUpdateForm(WebappOptionForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['name'].widget.choices = [(self.initial['name'], self.initial['name'])]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from orchestra.contrib.lists.models import List
|
||||
from orchestra.contrib.domains.models import Domain
|
||||
|
||||
class MailingUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = List
|
||||
fields = ("is_active", "name", "address_name", "address_domain")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
qs = Domain.objects.filter(account=self.user)
|
||||
self.fields['address_domain'].queryset = qs
|
||||
self.fields['address_name'].help_text = _("Additional address besides the default <name>@grups.pangea.org")
|
||||
self.fields['name'].widget.attrs['readonly'] = True
|
||||
|
||||
|
||||
class MailingCreateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = List
|
||||
fields = ("name", "address_name", "address_domain", "admin_email")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
qs = Domain.objects.filter(account=self.user)
|
||||
self.fields['address_domain'].queryset = qs
|
||||
self.fields['address_name'].help_text = _("Additional address besides the default <name>@grups.pangea.org")
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super().save(commit=False)
|
||||
instance.account = self.user
|
||||
if commit:
|
||||
super().save(commit=True)
|
||||
return instance
|
|
@ -1,80 +0,0 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from django.views.generic.list import ListView
|
||||
from orchestra.contrib.musician.mixins import (CustomContextMixin, ExtendedPaginationMixin,
|
||||
UserTokenRequiredMixin)
|
||||
from django.views.generic.edit import (CreateView, DeleteView, FormView,
|
||||
UpdateView)
|
||||
|
||||
from orchestra.contrib.lists.models import List
|
||||
from orchestra.contrib.domains.models import Domain, Record
|
||||
from orchestra.contrib.lists.settings import LISTS_DEFAULT_DOMAIN
|
||||
|
||||
from .forms import MailingUpdateForm, MailingCreateForm
|
||||
|
||||
class MailingListsView(CustomContextMixin, UserTokenRequiredMixin, ListView):
|
||||
model = List
|
||||
template_name = "musician/mailinglist_list.html"
|
||||
extra_context = {
|
||||
# Translators: This message appears on the page title
|
||||
'title': _('Mailing lists'),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(account=self.request.user)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
domain_id = self.request.GET.get('domain')
|
||||
if domain_id:
|
||||
qs = Domain.objects.filter(account=self.request.user)
|
||||
context.update({
|
||||
'active_domain': get_object_or_404(qs, pk=domain_id),
|
||||
})
|
||||
context.update({'default_domain': LISTS_DEFAULT_DOMAIN})
|
||||
return context
|
||||
|
||||
def get_queryfilter(self):
|
||||
"""Retrieve query params (if any) to filter queryset"""
|
||||
domain_id = self.request.GET.get('domain')
|
||||
if domain_id:
|
||||
return {"address_domain_id": domain_id}
|
||||
return {}
|
||||
|
||||
class MailingUpdateView(CustomContextMixin, UserTokenRequiredMixin, UpdateView):
|
||||
model = List
|
||||
form_class = MailingUpdateForm
|
||||
template_name = "musician/mailinglist_form.html"
|
||||
|
||||
def get_queryset(self):
|
||||
qs = List.objects.filter(account=self.request.user)
|
||||
return qs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("musician:mailing-lists")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["user"] = self.request.user
|
||||
return kwargs
|
||||
|
||||
class MailingCreateView(CustomContextMixin, UserTokenRequiredMixin, CreateView):
|
||||
model = List
|
||||
form_class = MailingCreateForm
|
||||
template_name = "musician/mailinglist_form.html"
|
||||
success_url = reverse_lazy("musician:mailing-lists")
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs['user'] = self.request.user
|
||||
return kwargs
|
||||
|
||||
class MailingDeleteView(CustomContextMixin, UserTokenRequiredMixin, DeleteView):
|
||||
template_name = "musician/mailing_check_delete.html"
|
||||
model = List
|
||||
success_url = reverse_lazy("musician:mailing-lists")
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(account=self.request.user)
|
|
@ -58,9 +58,3 @@ MUSICIAN_EDIT_ENABLE_PHP_OPTIONS = Setting('MUSICIAN_EDIT_ENABLE_PHP_OPTIONS', (
|
|||
'post_max_size',
|
||||
'upload_max_filesize',
|
||||
))
|
||||
|
||||
MUSICIAN_WEBSITES_ENABLE_GROUP_DIRECTIVE = Setting('MUSICIAN_WEBSITES_ENABLE_GROUP_DIRECTIVE', (
|
||||
'HTTPD',
|
||||
),
|
||||
help_text="Valid groups: HTTPD, ModSecurity, SSL, SaaS"
|
||||
)
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
{% extends "musician/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<p>{% blocktrans %}Are you sure that you want remove the list: "{{ list }}"?{% endblocktrans %}</p>
|
||||
<p class="alert alert-warning"><strong>{% trans 'WARNING: This action cannot be undone.' %}</strong></p>
|
||||
<a class="btn btn-light mr-2" href="{% url 'musician:mailing-lists' %}">{% trans 'Cancel' %}</a>
|
||||
<input class="btn btn-danger" type="submit" value="{% trans 'Delete' %}">
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,40 +0,0 @@
|
|||
{% extends "musician/base.html" %}
|
||||
{% load bootstrap4 i18n %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<style>
|
||||
.form-check{
|
||||
background-color: #fff;
|
||||
padding: .375rem 2.0rem;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<a class="btn-arrow-left" href="{% url 'musician:mailing-lists' %}">{% trans "Go back" %}</a>
|
||||
|
||||
<h1 class="service-name">
|
||||
{% if form.instance.pk %}
|
||||
{% trans "Update list" %} <span class="font-weight-light">{{ list.name }}</span>
|
||||
{% else %}
|
||||
{% trans "Create list" %}
|
||||
{% endif %}
|
||||
</h1>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% buttons %}
|
||||
<a class="btn btn-light mr-2" href="{% url 'musician:mailing-lists' %}">{% 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:mailing-delete' view.kwargs.pk %}">{% trans "Delete" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
@ -13,10 +13,9 @@
|
|||
<colgroup>
|
||||
<col span="1" style="width: 13%;">
|
||||
<col span="1" style="width: 12%;">
|
||||
<col span="1" style="width: 40%;">
|
||||
<col span="1" style="width: 50%;">
|
||||
<col span="1" style="width: 15%;">
|
||||
<col span="1" style="width: 10%;">
|
||||
<col span="1" style="width: 10%;">
|
||||
</colgroup>
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
|
@ -25,7 +24,6 @@
|
|||
<th scope="col">Address</th>
|
||||
<th scope="col">Admin email</th>
|
||||
<th scope="col">Configure</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
@ -37,25 +35,12 @@
|
|||
{% else %}
|
||||
<td class="text-danger font-weight-bold">{% trans "Inactive" %}</td>
|
||||
{% endif %}
|
||||
<td>
|
||||
{% if resource.address %}
|
||||
{{ resource.address }}
|
||||
{% else %}
|
||||
{{ resource.name }}@{{ default_domain }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ resource.address_name}}</td>
|
||||
<td>{{ resource.admin_email }}</td>
|
||||
<td><a href="{{ resource.get_absolute_url }}" target="_blank" rel="noopener noreferrer">Mailman <i class="fas fa-external-link-alt"></i></a></td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-outline-warning" href="{% url 'musician:mailing-update' resource.id %}">
|
||||
<i class="fas fa-tools"></i></a>
|
||||
<a href="{% url 'musician:mailing-delete' resource.id %}">
|
||||
<i class="ml-3 text-danger fas fa-trash"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% include "musician/components/table_paginator.html" %}
|
||||
</table>
|
||||
<a class="btn btn-primary mt-4 mb-4" href="{% url 'musician:mailing-create' %}">{% trans "New list" %}</a>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% load bootstrap4 i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="service-name">{% trans "Change password for" %}: <span class="font-weight-light">{{ object.username }}</span></h1>
|
||||
<h1 class="service-name">{% trans "Change password" %}: <span class="font-weight-light">{{ object.name }}</span></h1>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
{% 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">
|
||||
{% if option.name in edit_allowed_PHP_options %}
|
||||
<a href="{% url 'musician:webapp-update-option' object.pk option.pk %}">
|
||||
<i class="ml-3 fas fa-edit"></i></a>
|
||||
{% endif %}
|
||||
<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 %}
|
|
@ -1,121 +0,0 @@
|
|||
{% 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 "WebApp Options for" %} {{ object.name }}</h1>
|
||||
<hr>
|
||||
|
||||
<table class="table service-list">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Server:</th>
|
||||
<td colspan="2">{{ object.target_server }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Type:</th>
|
||||
<td colspan="2">{{ object.type }}</td>
|
||||
</tr>
|
||||
{% if object.data.php_version %}
|
||||
<tr>
|
||||
<th scope="row">PHP:</th>
|
||||
<td colspan="2">{{ object.data.php_version }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th scope="row">SFTP user:</th>
|
||||
{% if object.sftpuser %}
|
||||
<td>{{ object.sftpuser }}</td>
|
||||
<td>
|
||||
<div class="d-flex justify-content-end">
|
||||
<a class="btn btn-outline-warning" href="{% url 'musician:webappuser-password' object.sftpuser.id %}">
|
||||
<i class="fas fa-key"></i> {% trans "Update password" %}</a>
|
||||
</div>
|
||||
</td>
|
||||
{% else %}
|
||||
<td>{{ object.account.main_systemuser }}</td>
|
||||
<td>
|
||||
<div class="d-flex justify-content-end">
|
||||
<a class="btn btn-outline-warning" href="{% url 'musician:systemuser-password' object.account.main_systemuser.id %}">
|
||||
<i class="fas fa-key"></i> {% trans "Update password" %}</a>
|
||||
</div>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Path:</th>
|
||||
<td colspan="2">{{ object.get_base_path }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- Si es una aplicacion con bbdd tipo WP/moodle/lime mostrar bbdd -->
|
||||
{% if object.data.db_name %}
|
||||
<table class="table service-list">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Database:" %} </th>
|
||||
<td>{{ object.data.db_name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "User Database:" %}</th>
|
||||
<td> {{ object.data.db_user }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">{% trans "Password:" %} </th>
|
||||
<td>{{ object.data.password }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
Initial database and App admin password. <br>
|
||||
Subsequent changes to the admin password will not be reflected.
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
<!-- Opciones PHP -->
|
||||
|
||||
<h3 class="service-name">{% trans "PHP settings" %}</h3>
|
||||
<hr>
|
||||
|
||||
|
||||
{% if object.options.all|length != 0 %}
|
||||
<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">
|
||||
{% if option.name in edit_allowed_PHP_options %}
|
||||
<a href="{% url 'musician:webapp-update-option' object.pk option.pk %}">
|
||||
<i class="ml-3 fas fa-edit"></i></a>
|
||||
{% endif %}
|
||||
<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>
|
||||
{% else %}
|
||||
<p>{% trans "This WebApp has PHP options by default, create one if you need it." %}</p>
|
||||
{% endif %}
|
||||
<a class="btn btn-primary mt-4 mb-4" href="{% url 'musician:webapp-add-option' object.pk %}">{% trans "Add new option" %}</a></td>
|
||||
|
||||
{% endblock %}
|
|
@ -2,7 +2,7 @@
|
|||
{% load bootstrap4 i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="service-name">{% trans "Change password for" %}: <span class="font-weight-light">{{ object.username }}</span></h1>
|
||||
<h1 class="service-name">{% trans "Change password" %}: <span class="font-weight-light">{{ object.name }}</span></h1>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
|
@ -31,6 +31,7 @@
|
|||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
{% include "musician/components/table_paginator.html" %}
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
|
@ -38,8 +38,11 @@
|
|||
<span class="sr-only">{{ website.is_active|yesno }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn-outline-warning" href="{% url 'musician:website-detail' website.id %}">
|
||||
<i class="fas fa-tools"></i></a>
|
||||
<!-- <a class="btn btn-outline-warning" href="#">
|
||||
<i class="fas fa-tools"></i></a> -->
|
||||
<button type="button" class="btn btn-outline-warning" data-toggle="modal" data-target="#exampleModal">
|
||||
<i class="fas fa-tools"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Fila oculta de webapp -->
|
||||
|
@ -52,7 +55,7 @@
|
|||
<td>Webapp Dir</td>
|
||||
<td>/home/{{ content.webapp.account }}/webapps/{{ content.webapp }}</td>
|
||||
<td class="text-right">
|
||||
<a class="btn btn-outline-secondary" href="{% url 'musician:webapp-detail' content.webapp.id %}">
|
||||
<a class="btn btn-outline-secondary" href="{% url 'musician:webapp-list'%}">
|
||||
<i class="fas fa-tools"></i></a>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -104,4 +107,22 @@
|
|||
|
||||
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLabel">Sorry!</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{% trans "This section is under development." %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -1,21 +0,0 @@
|
|||
{% extends "musician/base.html" %}
|
||||
{% load bootstrap4 i18n %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<a class="btn-arrow-left" href="{% url 'musician:website-detail' view.kwargs.pk %}">{% trans "Go back" %}</a>
|
||||
|
||||
<h1 class="service-name">
|
||||
{% trans "Add Option to" %} <span class="font-weight-light">{{ form.website.name }}</span>
|
||||
</h1>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% buttons %}
|
||||
<a class="btn btn-light mr-2" href="{% url 'musician:website-detail' view.kwargs.pk %}">{% trans "Cancel" %}</a>
|
||||
<button type="submit" class="btn btn-secondary">{% trans "Save" %}</button>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,97 +0,0 @@
|
|||
{% extends "musician/base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<a class="btn-arrow-left" href="{% url 'musician:website-list' %}">{% trans "Go back" %}</a>
|
||||
|
||||
<h1 class="service-name">{% trans "WebSite Options for" %} {{ object.name }}</h1>
|
||||
<hr>
|
||||
|
||||
<table class="table service-list">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th scope="row">Server:</th>
|
||||
<td>{{ object.target_server }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Domains:</th>
|
||||
<td>
|
||||
{% for domain in object.domains.all %}
|
||||
{{ domain }} <br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">active:</th>
|
||||
<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>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Protocol:</th>
|
||||
<td>{{ object.protocol }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<a class="btn btn-primary mt-4 mb-4" href="{% url 'musician:website-update' object.pk %}">{% trans "Edit options" %}</a></td>
|
||||
|
||||
<!-- Contents -->
|
||||
<h3 class="service-name">{% trans "Contents" %}</h3>
|
||||
<hr>
|
||||
|
||||
<p>{% trans "Webapps assigned to this website" %}</p>
|
||||
<table class="table service-list">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th scope="col">{% trans "Webapp" %}</th>
|
||||
<th scope="col">{% trans "Type " %}</th>
|
||||
<th scope="col">{% trans "Path" %}</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for option in content %}
|
||||
<tr>
|
||||
<td>{{ option.webapp }}</td>
|
||||
<td>{{ option.webapp.get_type_display }}</td>
|
||||
<td>{{ option.path }}</td>
|
||||
<td class="text-right">
|
||||
<a href="{% url 'musician:website-delete-content' 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:website-add-content' object.pk %}">{% trans "Assigned new Webapp" %}</a></td>
|
||||
|
||||
<!-- Directives -->
|
||||
<h3 class="service-name">{% trans "Directives" %}</h3>
|
||||
<hr>
|
||||
|
||||
<p>{% trans "Options assigned to this Website" %} </p>
|
||||
<table class="table service-list">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th scope="col">{% trans "Name" %}</th>
|
||||
<th scope="col">{% trans "Value" %}</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for option in directives %}
|
||||
<tr>
|
||||
<td>{{ option.name }}</td>
|
||||
<td>{{ option.value }}</td>
|
||||
<td class="text-right">
|
||||
<a href="{% url 'musician:website-delete-directive' 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:website-add-directive' object.pk %}">{% trans "Add new directive" %}</a></td>
|
||||
|
||||
{% endblock %}
|
|
@ -1,31 +0,0 @@
|
|||
{% extends "musician/base.html" %}
|
||||
{% load bootstrap4 i18n %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<style>
|
||||
.form-check{
|
||||
background-color: #fff;
|
||||
padding: .375rem 2.0rem;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<a class="btn-arrow-left" href="{% url 'musician:website-detail' view.kwargs.pk %}">{% trans "Go back" %}</a>
|
||||
|
||||
<h1 class="service-name">
|
||||
{% trans "Update Option of Website" %} <span class="font-weight-light">{{ website.name }}</span>
|
||||
</h1>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
{% buttons %}
|
||||
<a class="btn btn-light mr-2" href="{% url 'musician:website-detail' view.kwargs.pk %}">{% trans "Cancel" %}</a>
|
||||
<button type="submit" class="btn btn-secondary">{% trans "Save" %}</button>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
{% 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>
|
||||
{% if object.name %}
|
||||
<pre>{{ object.name }} {{ object.value }}</pre>
|
||||
{% else %}
|
||||
<pre>Webapp {{ object.webapp }} assigned in website {{ object.website }}</pre>
|
||||
{% endif %}
|
||||
<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:website-detail' view.kwargs.pk %}">{% trans 'Cancel' %}</a>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -25,42 +25,26 @@ urlpatterns = [
|
|||
|
||||
path('billing/', views.BillingView.as_view(), name='billing'),
|
||||
path('bills/<int:pk>/download/', views.BillDownloadView.as_view(), name='bill-download'),
|
||||
|
||||
path('profile/', views.ProfileView.as_view(), name='profile'),
|
||||
path('profile/setLang/<code>', views.profile_set_language, name='profile-set-lang'),
|
||||
|
||||
path('address/', views.AddressListView.as_view(), name='address-list'),
|
||||
path('address/new/', views.MailCreateView.as_view(), name='address-create'),
|
||||
path('address/<int:pk>/', views.MailUpdateView.as_view(), name='address-update'),
|
||||
path('address/<int:pk>/delete/', views.AddressDeleteView.as_view(), name='address-delete'),
|
||||
|
||||
path('mailboxes/', views.MailboxListView.as_view(), name='mailbox-list'),
|
||||
path('mailboxes/new/', views.MailboxCreateView.as_view(), name='mailbox-create'),
|
||||
path('mailboxes/<int:pk>/', views.MailboxUpdateView.as_view(), name='mailbox-update'),
|
||||
path('mailboxes/<int:pk>/delete/', views.MailboxDeleteView.as_view(), name='mailbox-delete'),
|
||||
path('mailboxes/<int:pk>/change-password/', views.MailboxChangePasswordView.as_view(), name='mailbox-password'),
|
||||
|
||||
path('mailing-lists/', views.MailingListsView.as_view(), name='mailing-lists'),
|
||||
path('mailing-lists/<int:pk>/', views.MailingUpdateView.as_view(), name='mailing-update'),
|
||||
path('mailing-lists/new/', views.MailingCreateView.as_view(), name='mailing-create'),
|
||||
path('mailing-lists/<int:pk>/delete/', views.MailingDeleteView.as_view(), name='mailing-delete'),
|
||||
|
||||
path('databases/', views.DatabaseListView.as_view(), name='database-list'),
|
||||
|
||||
path('saas/', views.SaasListView.as_view(), name='saas-list'),
|
||||
|
||||
path('webappusers/', views.WebappUserListView.as_view(), name='webappuser-list'),
|
||||
path('webappuser/<int:pk>/change-password/', views.WebappUserChangePasswordView.as_view(), name='webappuser-password'),
|
||||
path('systemusers/', views.SystemUserListView.as_view(), name='systemuser-list'),
|
||||
path('systemuser/<int:pk>/change-password/', views.SystemUserChangePasswordView.as_view(), name='systemuser-password'),
|
||||
|
||||
path('websites/', views.WebsiteListView.as_view(), name='website-list'),
|
||||
path('websites/<int:pk>/', views.WebsiteDetailView.as_view(), name='website-detail'),
|
||||
path('websites/<int:pk>/edit/', views.WebsiteUpdateView.as_view(), name='website-update'),
|
||||
path('websites/<int:pk>/add-content/', views.WebsiteAddContentView.as_view(), name='website-add-content'),
|
||||
path('websites/<int:pk>/add-directive/', views.WebsiteAddDirectiveView.as_view(), name='website-add-directive'),
|
||||
path('websites/<int:pk>/content/<int:content_pk>/delete/', views.WebsiteDeleteContentView.as_view(), name='website-delete-content'),
|
||||
path('websites/<int:pk>/directive/<int:directive_pk>/delete/', views.WebsiteDeleteDirectiveView.as_view(), name='website-delete-directive'),
|
||||
|
||||
path('webapps/', views.WebappListView.as_view(), name='webapp-list'),
|
||||
path('webapps/<int:pk>/', views.WebappDetailView.as_view(), name='webapp-detail'),
|
||||
|
|
|
@ -33,13 +33,15 @@ from orchestra.contrib.mailboxes.models import Address, Mailbox
|
|||
from orchestra.contrib.resources.models import Resource, ResourceData
|
||||
from orchestra.contrib.saas.models import SaaS
|
||||
from orchestra.contrib.systemusers.models import WebappUsers, SystemUser
|
||||
from orchestra.contrib.websites.models import Website
|
||||
from orchestra.contrib.webapps.models import WebApp, WebAppOption
|
||||
from orchestra.utils.html import html_to_pdf
|
||||
|
||||
from .auth import logout as auth_logout
|
||||
from .forms import (LoginForm, MailboxChangePasswordForm, MailboxCreateForm,
|
||||
MailboxSearchForm, MailboxUpdateForm, MailForm,
|
||||
RecordCreateForm, RecordUpdateForm, WebappUsersChangePasswordForm,
|
||||
SystemUsersChangePasswordForm)
|
||||
SystemUsersChangePasswordForm, WebappOptionCreateForm, WebappOptionUpdateForm)
|
||||
from .mixins import (CustomContextMixin, ExtendedPaginationMixin,
|
||||
UserTokenRequiredMixin)
|
||||
from .models import Address as AddressService
|
||||
|
@ -50,10 +52,6 @@ from .models import MailinglistService, SaasService
|
|||
from .settings import ALLOWED_RESOURCES, MUSICIAN_EDIT_ENABLE_PHP_OPTIONS
|
||||
from .utils import get_bootstraped_percent
|
||||
|
||||
from .webapps.views import *
|
||||
from .websites.views import *
|
||||
from .lists.views import *
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -316,6 +314,37 @@ class AddressDeleteView(CustomContextMixin, UserTokenRequiredMixin, DeleteView):
|
|||
return self.model.objects.filter(account=self.request.user)
|
||||
|
||||
|
||||
class MailingListsView(ServiceListView):
|
||||
service_class = MailinglistService
|
||||
model = List
|
||||
template_name = "musician/mailinglist_list.html"
|
||||
extra_context = {
|
||||
# Translators: This message appears on the page title
|
||||
'title': _('Mailing lists'),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(account=self.request.user).order_by("name")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
domain_id = self.request.GET.get('domain')
|
||||
if domain_id:
|
||||
qs = Domain.objects.filter(account=self.request.user)
|
||||
context.update({
|
||||
'active_domain': get_object_or_404(qs, pk=domain_id)
|
||||
})
|
||||
return context
|
||||
|
||||
def get_queryfilter(self):
|
||||
"""Retrieve query params (if any) to filter queryset"""
|
||||
domain_id = self.request.GET.get('domain')
|
||||
if domain_id:
|
||||
return {"address_domain_id": domain_id}
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
class MailboxListView(ServiceListView):
|
||||
service_class = MailboxService
|
||||
model = Mailbox
|
||||
|
@ -591,14 +620,14 @@ class LogoutView(RedirectView):
|
|||
|
||||
class WebappUserListView(ServiceListView):
|
||||
model = WebappUsers
|
||||
template_name = "musician/webapps/webappuser_list.html"
|
||||
template_name = "musician/webappuser_list.html"
|
||||
extra_context = {
|
||||
# Translators: This message appears on the page title
|
||||
'title': _('Webapp users'),
|
||||
}
|
||||
|
||||
class WebappUserChangePasswordView(CustomContextMixin, UserTokenRequiredMixin, UpdateView):
|
||||
template_name = "musician/webapps/webappuser_change_password.html"
|
||||
template_name = "musician/webappuser_change_password.html"
|
||||
model = WebappUsers
|
||||
form_class = WebappUsersChangePasswordForm
|
||||
success_url = reverse_lazy("musician:webappuser-list")
|
||||
|
@ -624,3 +653,103 @@ class SystemUserChangePasswordView(CustomContextMixin, UserTokenRequiredMixin, U
|
|||
def get_queryset(self):
|
||||
return self.model.objects.filter(account=self.request.user)
|
||||
|
||||
class WebsiteListView(CustomContextMixin, UserTokenRequiredMixin, ListView):
|
||||
model = Website
|
||||
template_name = "musician/website_list.html"
|
||||
extra_context = {
|
||||
# Translators: This message appears on the page title
|
||||
'title': _('Websites'),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(account=self.request.user)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'description': _("A website is the place where a domain is associated with the directory where the web files are located. (WebApp)"),
|
||||
})
|
||||
return context
|
||||
|
||||
class WebappListView(CustomContextMixin, UserTokenRequiredMixin, ListView):
|
||||
model = WebApp
|
||||
template_name = "musician/webapp_list.html"
|
||||
extra_context = {
|
||||
# Translators: This message appears on the page title
|
||||
'title': _('Webapps'),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(account=self.request.user)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'description': _("A web app is the directory where your website is stored. Through SFTP, you can access this directory and upload/edit/delete files."),
|
||||
'description2': _("Each Webapp has its own SFTP user, which is created automatically when the Webapp is created.")
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
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)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'edit_allowed_PHP_options': MUSICIAN_EDIT_ENABLE_PHP_OPTIONS
|
||||
})
|
||||
return context
|
||||
|
||||
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"]})
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
object = self.get_object()
|
||||
response = super().delete(request, *args, **kwargs)
|
||||
object.webapp.save()
|
||||
return response
|
||||
|
||||
|
||||
class WebappUpdateOptionView(CustomContextMixin, UserTokenRequiredMixin, UpdateView):
|
||||
model = WebAppOption
|
||||
form_class = WebappOptionUpdateForm
|
||||
template_name = "musician/webapp_option_form.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"]})
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
from django import forms
|
||||
from orchestra.forms.widgets import DynamicHelpTextSelect
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
from orchestra.contrib.webapps.models import WebApp, WebAppOption
|
||||
from orchestra.contrib.webapps.options import AppOption
|
||||
from orchestra.contrib.webapps.types import AppType
|
||||
|
||||
from orchestra.contrib.musician.settings import MUSICIAN_EDIT_ENABLE_PHP_OPTIONS
|
||||
|
||||
|
||||
|
||||
class WebappOptionForm(forms.ModelForm):
|
||||
|
||||
OPTIONS_HELP_TEXT = {
|
||||
op.name: force_str(op.help_text) for op in AppOption.get_plugins()
|
||||
}
|
||||
|
||||
class Meta:
|
||||
model = WebAppOption
|
||||
fields = ("name", "value")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
try:
|
||||
self.webapp = kwargs.pop('webapp')
|
||||
super().__init__(*args, **kwargs)
|
||||
except:
|
||||
super().__init__(*args, **kwargs)
|
||||
self.webapp = self.instance.webapp
|
||||
|
||||
target = 'this.id.replace("name", "value")'
|
||||
self.fields['name'].widget.attrs = DynamicHelpTextSelect(target, self.OPTIONS_HELP_TEXT).attrs
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super().save(commit=False)
|
||||
instance.webapp = self.webapp
|
||||
if commit:
|
||||
super().save(commit=True)
|
||||
self.webapp.save()
|
||||
return instance
|
||||
|
||||
|
||||
class WebappOptionCreateForm(WebappOptionForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
plugin = AppType.get(self.webapp.type)
|
||||
choices = list(plugin.get_group_options_choices())
|
||||
for grupo, opciones in enumerate(choices):
|
||||
if isinstance(opciones[1], list):
|
||||
nueva_lista = [opc for opc in opciones[1] if opc[0] in MUSICIAN_EDIT_ENABLE_PHP_OPTIONS]
|
||||
choices[grupo] = (opciones[0], nueva_lista)
|
||||
self.fields['name'].widget.choices = choices
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
name = self.cleaned_data.get("name")
|
||||
if WebAppOption.objects.filter(webapp=self.webapp, name=name).exists():
|
||||
raise ValidationError(_("This option already exist."))
|
||||
return cleaned_data
|
||||
|
||||
class WebappOptionUpdateForm(WebappOptionForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['name'].widget.choices = [(self.initial['name'], self.initial['name'])]
|
|
@ -1,102 +0,0 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||
from django.views.generic.list import ListView
|
||||
|
||||
from orchestra.contrib.musician.mixins import (CustomContextMixin, ExtendedPaginationMixin,
|
||||
UserTokenRequiredMixin)
|
||||
|
||||
from orchestra.contrib.webapps.models import WebApp, WebAppOption
|
||||
from .forms import WebappOptionCreateForm, WebappOptionUpdateForm
|
||||
|
||||
from orchestra.contrib.musician.settings import MUSICIAN_EDIT_ENABLE_PHP_OPTIONS
|
||||
|
||||
|
||||
|
||||
|
||||
class WebappListView(CustomContextMixin, UserTokenRequiredMixin, ListView):
|
||||
model = WebApp
|
||||
template_name = "musician/webapps/webapp_list.html"
|
||||
extra_context = {
|
||||
# Translators: This message appears on the page title
|
||||
'title': _('Webapps'),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(account=self.request.user)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'description': _("A web app is the directory where your website is stored. Through SFTP, you can access this directory and upload/edit/delete files."),
|
||||
'description2': _("Each Webapp has its own SFTP user, which is created automatically when the Webapp is created.")
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class WebappDetailView(CustomContextMixin, UserTokenRequiredMixin, DetailView):
|
||||
template_name = "musician/webapps/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)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'edit_allowed_PHP_options': MUSICIAN_EDIT_ENABLE_PHP_OPTIONS
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class WebappAddOptionView(CustomContextMixin, UserTokenRequiredMixin, CreateView):
|
||||
model = WebAppOption
|
||||
form_class = WebappOptionCreateForm
|
||||
template_name = "musician/webapps/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/webapps/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"]})
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
object = self.get_object()
|
||||
response = super().delete(request, *args, **kwargs)
|
||||
object.webapp.save()
|
||||
return response
|
||||
|
||||
|
||||
class WebappUpdateOptionView(CustomContextMixin, UserTokenRequiredMixin, UpdateView):
|
||||
model = WebAppOption
|
||||
form_class = WebappOptionUpdateForm
|
||||
template_name = "musician/webapps/webapp_option_form.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"]})
|
|
@ -1,121 +0,0 @@
|
|||
from django import forms
|
||||
from orchestra.forms.widgets import DynamicHelpTextSelect
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
from orchestra.contrib.websites.directives import SiteDirective
|
||||
from orchestra.contrib.websites.models import Website, Content, WebsiteDirective
|
||||
from orchestra.contrib.webapps.models import WebApp
|
||||
from orchestra.contrib.domains.models import Domain
|
||||
|
||||
from orchestra.contrib.musician.settings import MUSICIAN_WEBSITES_ENABLE_GROUP_DIRECTIVE
|
||||
|
||||
|
||||
class WebsiteUpdateForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = Website
|
||||
fields = ("is_active", "protocol", "domains")
|
||||
help_texts = {
|
||||
'domains': _('Hold down "Control", or "Command" on a Mac, to select more than one.')
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
# Excluir dominios de otros websites
|
||||
qs = Website.objects.filter(account=self.user).exclude(id=self.instance.id)
|
||||
used_domains = []
|
||||
for website in qs:
|
||||
dominios = website.domains.all()
|
||||
for dominio in dominios:
|
||||
used_domains.append(dominio)
|
||||
self.fields['domains'].queryset = Domain.objects.filter(account=self.user).exclude(name__in=used_domains)
|
||||
|
||||
|
||||
class WesiteContentCreateForm(forms.ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Content
|
||||
fields = ("webapp", "path")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.website = kwargs.pop('website')
|
||||
self.user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['webapp'].queryset = WebApp.objects.filter(account=self.user, target_server=self.website.target_server)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
path = self.cleaned_data.get("path")
|
||||
path = "/" if path == "" else path
|
||||
if Content.objects.filter(website=self.website, path=path).exists():
|
||||
self.add_error('path',_("This Path already exists on this Website."))
|
||||
return cleaned_data
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super().save(commit=False)
|
||||
instance.website = self.website
|
||||
if commit:
|
||||
super().save(commit=True)
|
||||
self.website.save()
|
||||
return instance
|
||||
|
||||
from collections import defaultdict
|
||||
from orchestra.contrib.websites.utils import normurlpath
|
||||
|
||||
class WesiteDirectiveCreateForm(forms.ModelForm):
|
||||
|
||||
DIRECTIVES_HELP_TEXT = {
|
||||
op.name: force_str(op.help_text) for op in SiteDirective.get_plugins()
|
||||
}
|
||||
|
||||
class Meta:
|
||||
model = WebsiteDirective
|
||||
fields = ("name", "value")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.website = kwargs.pop('website')
|
||||
# self.user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
target = 'this.id.replace("name", "value")'
|
||||
self.fields['name'].widget.attrs = DynamicHelpTextSelect(target, self.DIRECTIVES_HELP_TEXT).attrs
|
||||
self.fields['name'].choices = self.get_allow_choices()
|
||||
|
||||
def get_allow_choices(self):
|
||||
groups = MUSICIAN_WEBSITES_ENABLE_GROUP_DIRECTIVE
|
||||
yield (None, '-------')
|
||||
options = SiteDirective.get_option_groups()
|
||||
for grp in groups:
|
||||
if grp in options.keys():
|
||||
yield (grp, [(op.name, op.verbose_name) for op in options[grp]])
|
||||
|
||||
|
||||
|
||||
def clean(self):
|
||||
# Recoge todos los paths de directive y contents
|
||||
locations = set()
|
||||
for content in Content.objects.filter(website=self.website):
|
||||
location = content.path
|
||||
if location is not None:
|
||||
locations.add(location)
|
||||
for directive_obj in WebsiteDirective.objects.filter(website=self.website):
|
||||
location = directive_obj.value
|
||||
if location is not None:
|
||||
locations.add(normurlpath(location.split()[0]))
|
||||
# Comprueva que no se repitan
|
||||
directive = self.cleaned_data
|
||||
value = normurlpath(directive.get('value'))
|
||||
if value:
|
||||
value = value.split()[0]
|
||||
if value in locations:
|
||||
self.add_error('value', f"Location '{value}' already in use by other content/directive.")
|
||||
|
||||
def save(self, commit=True):
|
||||
instance = super().save(commit=False)
|
||||
instance.website = self.website
|
||||
if commit:
|
||||
super().save(commit=True)
|
||||
self.website.save()
|
||||
return instance
|
|
@ -1,140 +0,0 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse_lazy
|
||||
|
||||
from django.views.generic.base import RedirectView, TemplateView
|
||||
from django.views.generic.detail import DetailView
|
||||
from django.views.generic.edit import (CreateView, DeleteView, FormView,
|
||||
UpdateView)
|
||||
from django.views.generic.list import ListView
|
||||
|
||||
from orchestra.contrib.musician.mixins import (CustomContextMixin, ExtendedPaginationMixin,
|
||||
UserTokenRequiredMixin)
|
||||
|
||||
from orchestra.contrib.websites.models import Website, Content, WebsiteDirective
|
||||
from .forms import ( WebsiteUpdateForm, WesiteContentCreateForm,
|
||||
WesiteDirectiveCreateForm)
|
||||
|
||||
|
||||
class WebsiteListView(CustomContextMixin, UserTokenRequiredMixin, ListView):
|
||||
model = Website
|
||||
template_name = "musician/websites/website_list.html"
|
||||
extra_context = {
|
||||
# Translators: This message appears on the page title
|
||||
'title': _('Websites'),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return self.model.objects.filter(account=self.request.user)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context.update({
|
||||
'description': _("A website is the place where a domain is associated with the directory where the web files are located. (WebApp)"),
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class WebsiteDetailView(CustomContextMixin, UserTokenRequiredMixin, DetailView):
|
||||
template_name = "musician/websites/website_detail.html"
|
||||
extra_context = {
|
||||
# Translators: This message appears on the page title
|
||||
'title': _('website details'),
|
||||
}
|
||||
|
||||
def get_queryset(self):
|
||||
return Website.objects.filter(account=self.request.user)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
context['content'] = Content.objects.filter(website=self.object)
|
||||
context['directives'] = WebsiteDirective.objects.filter(website=self.object)
|
||||
return context
|
||||
|
||||
|
||||
class WebsiteUpdateView(CustomContextMixin, UserTokenRequiredMixin, UpdateView):
|
||||
model = Website
|
||||
form_class = WebsiteUpdateForm
|
||||
template_name = "musician/websites/website_form.html"
|
||||
|
||||
def get_queryset(self):
|
||||
qs = Website.objects.filter(account=self.request.user)
|
||||
return qs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("musician:website-detail", kwargs={"pk": self.kwargs["pk"]})
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs["user"] = self.request.user
|
||||
return kwargs
|
||||
|
||||
|
||||
class WebsiteDeleteContentView(CustomContextMixin, UserTokenRequiredMixin, DeleteView):
|
||||
model = Content
|
||||
template_name = "musician/websites/websiteoption_check_delete.html"
|
||||
pk_url_kwarg = "content_pk"
|
||||
|
||||
def get_queryset(self):
|
||||
qs = Content.objects.filter(website__account=self.request.user, website=self.kwargs["pk"])
|
||||
return qs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("musician:website-detail", kwargs={"pk": self.kwargs["pk"]})
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
object = self.get_object()
|
||||
response = super().delete(request, *args, **kwargs)
|
||||
object.website.save()
|
||||
return response
|
||||
|
||||
|
||||
class WebsiteDeleteDirectiveView(CustomContextMixin, UserTokenRequiredMixin, DeleteView):
|
||||
model = WebsiteDirective
|
||||
template_name = "musician/websites/websiteoption_check_delete.html"
|
||||
pk_url_kwarg = "directive_pk"
|
||||
|
||||
def get_queryset(self):
|
||||
qs = WebsiteDirective.objects.filter(website__account=self.request.user, website=self.kwargs["pk"])
|
||||
return qs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("musician:website-detail", kwargs={"pk": self.kwargs["pk"]})
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
object = self.get_object()
|
||||
response = super().delete(request, *args, **kwargs)
|
||||
object.website.save()
|
||||
return response
|
||||
|
||||
|
||||
class WebsiteAddContentView(CustomContextMixin, UserTokenRequiredMixin, CreateView):
|
||||
model = Content
|
||||
form_class = WesiteContentCreateForm
|
||||
template_name = "musician/websites/website_create_option_form.html"
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
website = get_object_or_404(Website, account=self.request.user, pk=self.kwargs["pk"])
|
||||
kwargs['website'] = website
|
||||
kwargs["user"] = self.request.user
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("musician:website-detail", kwargs={"pk": self.kwargs["pk"]})
|
||||
|
||||
|
||||
class WebsiteAddDirectiveView(CustomContextMixin, UserTokenRequiredMixin, CreateView):
|
||||
model = WebsiteDirective
|
||||
form_class = WesiteDirectiveCreateForm
|
||||
template_name = "musician/websites/website_create_option_form.html"
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
website = get_object_or_404(Website, account=self.request.user, pk=self.kwargs["pk"])
|
||||
kwargs['website'] = website
|
||||
return kwargs
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("musician:website-detail", kwargs={"pk": self.kwargs["pk"]})
|
||||
|
|
@ -4,7 +4,6 @@ from functools import lru_cache
|
|||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
from orchestra import plugins
|
||||
from orchestra.utils.python import import_class
|
||||
|
@ -56,7 +55,7 @@ class SiteDirective(plugins.Plugin, metaclass=plugins.PluginMount):
|
|||
yield (option.name, option.verbose_name)
|
||||
for group, options in options.items():
|
||||
yield (group, [(op.name, op.verbose_name) for op in options])
|
||||
|
||||
|
||||
def validate_uniqueness(self, directive, values, locations):
|
||||
""" Validates uniqueness location, name and value """
|
||||
errors = defaultdict(list)
|
||||
|
|
|
@ -67,7 +67,7 @@ class WebsiteDirectiveInlineFormSet(forms.models.BaseInlineFormSet):
|
|||
delete = form.cleaned_data.get('DELETE')
|
||||
if not delete and location is not None:
|
||||
locations.add(normurlpath(location))
|
||||
|
||||
|
||||
values = defaultdict(list)
|
||||
for form in self.forms:
|
||||
wdirective = form.instance
|
||||
|
|
Loading…
Reference in New Issue