Compare commits
11 Commits
ad11f64e52
...
8cd3605d48
Author | SHA1 | Date |
---|---|---|
Jorge Pastor | 8cd3605d48 | |
Jorge Pastor | e1d250c35f | |
Jorge Pastor | 281d50acc5 | |
Jorge Pastor | 54c6c59237 | |
Jorge Pastor | 9dc719abcb | |
Jorge Pastor | 0dc4352432 | |
Jorge Pastor | 475df0a2e5 | |
Jorge Pastor | 1ebf30db33 | |
Jorge Pastor | 4f258a9fe0 | |
Jorge Pastor | f3e3d45d60 | |
Jorge Pastor | 898dde9330 |
|
@ -29,6 +29,9 @@ class MySQLController(ServiceController):
|
||||||
# Create database and re-set permissions
|
# Create database and re-set permissions
|
||||||
mysql -e 'CREATE DATABASE `%(database)s`;' || true
|
mysql -e 'CREATE DATABASE `%(database)s`;' || true
|
||||||
mysql mysql -e 'DELETE FROM db WHERE db = "%(database)s";'\
|
mysql mysql -e 'DELETE FROM db WHERE db = "%(database)s";'\
|
||||||
|
|
||||||
|
# wait to create user
|
||||||
|
sleep 1.5
|
||||||
""") % context
|
""") % context
|
||||||
)
|
)
|
||||||
for user in database.users.all():
|
for user in database.users.all():
|
||||||
|
|
|
@ -145,8 +145,9 @@ class Domain(models.Model):
|
||||||
else:
|
else:
|
||||||
zone += subdomain.render_records()
|
zone += subdomain.render_records()
|
||||||
###darmengo 2021-03-25 add autoconfig
|
###darmengo 2021-03-25 add autoconfig
|
||||||
|
# TODO: que se asigne esta ip automaticamente
|
||||||
if self.has_default_mx():
|
if self.has_default_mx():
|
||||||
zone += 'autoconfig.{}. 30m IN A 109.69.8.133\n'.format(self.name)
|
zone += 'autoconfig.{}. 30m IN A 45.150.186.197\n'.format(self.name)
|
||||||
###END darmengo 2021-03-25 add autoconfig
|
###END darmengo 2021-03-25 add autoconfig
|
||||||
for subdomain in sorted(tail, key=lambda x: len(x.name), reverse=True):
|
for subdomain in sorted(tail, key=lambda x: len(x.name), reverse=True):
|
||||||
zone += subdomain.render_records()
|
zone += subdomain.render_records()
|
||||||
|
|
|
@ -2,8 +2,6 @@ from django import forms
|
||||||
from django.contrib.auth.forms import AuthenticationForm
|
from django.contrib.auth.forms import AuthenticationForm
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import gettext_lazy as _
|
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
|
from django.contrib.auth.hashers import make_password
|
||||||
|
|
||||||
|
@ -11,12 +9,8 @@ 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 orchestra.contrib.webapps.options import AppOption
|
|
||||||
from orchestra.contrib.webapps.types import AppType
|
|
||||||
|
|
||||||
from . import api
|
from . import api
|
||||||
from .settings import MUSICIAN_EDIT_ENABLE_PHP_OPTIONS
|
|
||||||
|
|
||||||
|
|
||||||
class LoginForm(AuthenticationForm):
|
class LoginForm(AuthenticationForm):
|
||||||
|
@ -208,63 +202,3 @@ class SystemUsersChangePasswordForm(ChangePasswordForm):
|
||||||
fields = ("password",)
|
fields = ("password",)
|
||||||
model = SystemUser
|
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'])]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
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
|
|
@ -0,0 +1,80 @@
|
||||||
|
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,3 +58,9 @@ MUSICIAN_EDIT_ENABLE_PHP_OPTIONS = Setting('MUSICIAN_EDIT_ENABLE_PHP_OPTIONS', (
|
||||||
'post_max_size',
|
'post_max_size',
|
||||||
'upload_max_filesize',
|
'upload_max_filesize',
|
||||||
))
|
))
|
||||||
|
|
||||||
|
MUSICIAN_WEBSITES_ENABLE_GROUP_DIRECTIVE = Setting('MUSICIAN_WEBSITES_ENABLE_GROUP_DIRECTIVE', (
|
||||||
|
'HTTPD',
|
||||||
|
),
|
||||||
|
help_text="Valid groups: HTTPD, ModSecurity, SSL, SaaS"
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,40 @@
|
||||||
|
{% 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,9 +13,10 @@
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col span="1" style="width: 13%;">
|
<col span="1" style="width: 13%;">
|
||||||
<col span="1" style="width: 12%;">
|
<col span="1" style="width: 12%;">
|
||||||
<col span="1" style="width: 50%;">
|
<col span="1" style="width: 40%;">
|
||||||
<col span="1" style="width: 15%;">
|
<col span="1" style="width: 15%;">
|
||||||
<col span="1" style="width: 10%;">
|
<col span="1" style="width: 10%;">
|
||||||
|
<col span="1" style="width: 10%;">
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<thead class="thead-dark">
|
<thead class="thead-dark">
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
<th scope="col">Address</th>
|
<th scope="col">Address</th>
|
||||||
<th scope="col">Admin email</th>
|
<th scope="col">Admin email</th>
|
||||||
<th scope="col">Configure</th>
|
<th scope="col">Configure</th>
|
||||||
|
<th></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -35,12 +37,25 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
<td class="text-danger font-weight-bold">{% trans "Inactive" %}</td>
|
<td class="text-danger font-weight-bold">{% trans "Inactive" %}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td>{{ resource.address_name}}</td>
|
<td>
|
||||||
|
{% if resource.address %}
|
||||||
|
{{ resource.address }}
|
||||||
|
{% else %}
|
||||||
|
{{ resource.name }}@{{ default_domain }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>{{ resource.admin_email }}</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><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>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
{% include "musician/components/table_paginator.html" %}
|
|
||||||
</table>
|
</table>
|
||||||
|
<a class="btn btn-primary mt-4 mb-4" href="{% url 'musician:mailing-create' %}">{% trans "New list" %}</a>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
{% load bootstrap4 i18n %}
|
{% load bootstrap4 i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1 class="service-name">{% trans "Change password" %}: <span class="font-weight-light">{{ object.name }}</span></h1>
|
<h1 class="service-name">{% trans "Change password for" %}: <span class="font-weight-light">{{ object.username }}</span></h1>
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
@ -1,46 +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 "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 %}
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
{% 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 %}
|
{% load bootstrap4 i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1 class="service-name">{% trans "Change password" %}: <span class="font-weight-light">{{ object.name }}</span></h1>
|
<h1 class="service-name">{% trans "Change password for" %}: <span class="font-weight-light">{{ object.username }}</span></h1>
|
||||||
|
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
|
@ -31,7 +31,6 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
{% include "musician/components/table_paginator.html" %}
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -0,0 +1,21 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,97 @@
|
||||||
|
{% 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 %}
|
|
@ -0,0 +1,31 @@
|
||||||
|
{% 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 %}
|
||||||
|
|
|
@ -38,11 +38,8 @@
|
||||||
<span class="sr-only">{{ website.is_active|yesno }}</span>
|
<span class="sr-only">{{ website.is_active|yesno }}</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<!-- <a class="btn btn-outline-warning" href="#">
|
<a class="btn btn-outline-warning" href="{% url 'musician:website-detail' website.id %}">
|
||||||
<i class="fas fa-tools"></i></a> -->
|
<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>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<!-- Fila oculta de webapp -->
|
<!-- Fila oculta de webapp -->
|
||||||
|
@ -55,7 +52,7 @@
|
||||||
<td>Webapp Dir</td>
|
<td>Webapp Dir</td>
|
||||||
<td>/home/{{ content.webapp.account }}/webapps/{{ content.webapp }}</td>
|
<td>/home/{{ content.webapp.account }}/webapps/{{ content.webapp }}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<a class="btn btn-outline-secondary" href="{% url 'musician:webapp-list'%}">
|
<a class="btn btn-outline-secondary" href="{% url 'musician:webapp-detail' content.webapp.id %}">
|
||||||
<i class="fas fa-tools"></i></a>
|
<i class="fas fa-tools"></i></a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -107,22 +104,4 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- 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 %}
|
{% endblock %}
|
|
@ -0,0 +1,17 @@
|
||||||
|
{% 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,26 +25,42 @@ urlpatterns = [
|
||||||
|
|
||||||
path('billing/', views.BillingView.as_view(), name='billing'),
|
path('billing/', views.BillingView.as_view(), name='billing'),
|
||||||
path('bills/<int:pk>/download/', views.BillDownloadView.as_view(), name='bill-download'),
|
path('bills/<int:pk>/download/', views.BillDownloadView.as_view(), name='bill-download'),
|
||||||
|
|
||||||
path('profile/', views.ProfileView.as_view(), name='profile'),
|
path('profile/', views.ProfileView.as_view(), name='profile'),
|
||||||
path('profile/setLang/<code>', views.profile_set_language, name='profile-set-lang'),
|
path('profile/setLang/<code>', views.profile_set_language, name='profile-set-lang'),
|
||||||
|
|
||||||
path('address/', views.AddressListView.as_view(), name='address-list'),
|
path('address/', views.AddressListView.as_view(), name='address-list'),
|
||||||
path('address/new/', views.MailCreateView.as_view(), name='address-create'),
|
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>/', views.MailUpdateView.as_view(), name='address-update'),
|
||||||
path('address/<int:pk>/delete/', views.AddressDeleteView.as_view(), name='address-delete'),
|
path('address/<int:pk>/delete/', views.AddressDeleteView.as_view(), name='address-delete'),
|
||||||
|
|
||||||
path('mailboxes/', views.MailboxListView.as_view(), name='mailbox-list'),
|
path('mailboxes/', views.MailboxListView.as_view(), name='mailbox-list'),
|
||||||
path('mailboxes/new/', views.MailboxCreateView.as_view(), name='mailbox-create'),
|
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>/', views.MailboxUpdateView.as_view(), name='mailbox-update'),
|
||||||
path('mailboxes/<int:pk>/delete/', views.MailboxDeleteView.as_view(), name='mailbox-delete'),
|
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('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/', 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('databases/', views.DatabaseListView.as_view(), name='database-list'),
|
||||||
|
|
||||||
path('saas/', views.SaasListView.as_view(), name='saas-list'),
|
path('saas/', views.SaasListView.as_view(), name='saas-list'),
|
||||||
|
|
||||||
path('webappusers/', views.WebappUserListView.as_view(), name='webappuser-list'),
|
path('webappusers/', views.WebappUserListView.as_view(), name='webappuser-list'),
|
||||||
path('webappuser/<int:pk>/change-password/', views.WebappUserChangePasswordView.as_view(), name='webappuser-password'),
|
path('webappuser/<int:pk>/change-password/', views.WebappUserChangePasswordView.as_view(), name='webappuser-password'),
|
||||||
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('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/', views.WebappListView.as_view(), name='webapp-list'),
|
||||||
path('webapps/<int:pk>/', views.WebappDetailView.as_view(), name='webapp-detail'),
|
path('webapps/<int:pk>/', views.WebappDetailView.as_view(), name='webapp-detail'),
|
||||||
|
|
|
@ -33,15 +33,13 @@ from orchestra.contrib.mailboxes.models import Address, Mailbox
|
||||||
from orchestra.contrib.resources.models import Resource, ResourceData
|
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.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, WebappOptionCreateForm, WebappOptionUpdateForm)
|
SystemUsersChangePasswordForm)
|
||||||
from .mixins import (CustomContextMixin, ExtendedPaginationMixin,
|
from .mixins import (CustomContextMixin, ExtendedPaginationMixin,
|
||||||
UserTokenRequiredMixin)
|
UserTokenRequiredMixin)
|
||||||
from .models import Address as AddressService
|
from .models import Address as AddressService
|
||||||
|
@ -52,6 +50,10 @@ from .models import MailinglistService, SaasService
|
||||||
from .settings import ALLOWED_RESOURCES, MUSICIAN_EDIT_ENABLE_PHP_OPTIONS
|
from .settings import ALLOWED_RESOURCES, MUSICIAN_EDIT_ENABLE_PHP_OPTIONS
|
||||||
from .utils import get_bootstraped_percent
|
from .utils import get_bootstraped_percent
|
||||||
|
|
||||||
|
from .webapps.views import *
|
||||||
|
from .websites.views import *
|
||||||
|
from .lists.views import *
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -314,37 +316,6 @@ class AddressDeleteView(CustomContextMixin, UserTokenRequiredMixin, DeleteView):
|
||||||
return self.model.objects.filter(account=self.request.user)
|
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):
|
class MailboxListView(ServiceListView):
|
||||||
service_class = MailboxService
|
service_class = MailboxService
|
||||||
model = Mailbox
|
model = Mailbox
|
||||||
|
@ -620,14 +591,14 @@ class LogoutView(RedirectView):
|
||||||
|
|
||||||
class WebappUserListView(ServiceListView):
|
class WebappUserListView(ServiceListView):
|
||||||
model = WebappUsers
|
model = WebappUsers
|
||||||
template_name = "musician/webappuser_list.html"
|
template_name = "musician/webapps/webappuser_list.html"
|
||||||
extra_context = {
|
extra_context = {
|
||||||
# Translators: This message appears on the page title
|
# Translators: This message appears on the page title
|
||||||
'title': _('Webapp users'),
|
'title': _('Webapp users'),
|
||||||
}
|
}
|
||||||
|
|
||||||
class WebappUserChangePasswordView(CustomContextMixin, UserTokenRequiredMixin, UpdateView):
|
class WebappUserChangePasswordView(CustomContextMixin, UserTokenRequiredMixin, UpdateView):
|
||||||
template_name = "musician/webappuser_change_password.html"
|
template_name = "musician/webapps/webappuser_change_password.html"
|
||||||
model = WebappUsers
|
model = WebappUsers
|
||||||
form_class = WebappUsersChangePasswordForm
|
form_class = WebappUsersChangePasswordForm
|
||||||
success_url = reverse_lazy("musician:webappuser-list")
|
success_url = reverse_lazy("musician:webappuser-list")
|
||||||
|
@ -653,103 +624,3 @@ class SystemUserChangePasswordView(CustomContextMixin, UserTokenRequiredMixin, U
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return self.model.objects.filter(account=self.request.user)
|
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"]})
|
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
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'])]
|
|
@ -0,0 +1,102 @@
|
||||||
|
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"]})
|
|
@ -0,0 +1,121 @@
|
||||||
|
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
|
|
@ -0,0 +1,140 @@
|
||||||
|
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,6 +4,7 @@ from functools import lru_cache
|
||||||
|
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.utils.encoding import force_str
|
||||||
|
|
||||||
from orchestra import plugins
|
from orchestra import plugins
|
||||||
from orchestra.utils.python import import_class
|
from orchestra.utils.python import import_class
|
||||||
|
@ -55,7 +56,7 @@ class SiteDirective(plugins.Plugin, metaclass=plugins.PluginMount):
|
||||||
yield (option.name, option.verbose_name)
|
yield (option.name, option.verbose_name)
|
||||||
for group, options in options.items():
|
for group, options in options.items():
|
||||||
yield (group, [(op.name, op.verbose_name) for op in options])
|
yield (group, [(op.name, op.verbose_name) for op in options])
|
||||||
|
|
||||||
def validate_uniqueness(self, directive, values, locations):
|
def validate_uniqueness(self, directive, values, locations):
|
||||||
""" Validates uniqueness location, name and value """
|
""" Validates uniqueness location, name and value """
|
||||||
errors = defaultdict(list)
|
errors = defaultdict(list)
|
||||||
|
|
|
@ -67,7 +67,7 @@ class WebsiteDirectiveInlineFormSet(forms.models.BaseInlineFormSet):
|
||||||
delete = form.cleaned_data.get('DELETE')
|
delete = form.cleaned_data.get('DELETE')
|
||||||
if not delete and location is not None:
|
if not delete and location is not None:
|
||||||
locations.add(normurlpath(location))
|
locations.add(normurlpath(location))
|
||||||
|
|
||||||
values = defaultdict(list)
|
values = defaultdict(list)
|
||||||
for form in self.forms:
|
for form in self.forms:
|
||||||
wdirective = form.instance
|
wdirective = form.instance
|
||||||
|
|
Loading…
Reference in New Issue