Added support from domain SOA record massive editing and phplist password changing
This commit is contained in:
parent
0d9058d266
commit
708758880d
3
TODO.md
3
TODO.md
|
@ -426,7 +426,4 @@ Case
|
|||
|
||||
# bill changelist: dates: (closed_on, created_on, updated_on)
|
||||
|
||||
|
||||
# Add record modeladmin action: select domains + add records (formset) to selected domains
|
||||
|
||||
# Resource data inline show info: link to monitor data, and history chart: link to monitor data of each item
|
||||
|
|
|
@ -129,4 +129,4 @@ def disable(modeladmin, request, queryset):
|
|||
num)
|
||||
modeladmin.message_user(request, msg)
|
||||
disable.url_name = 'disable'
|
||||
disable.verbose_name = _("Disable")
|
||||
disable.short_description = _("Disable")
|
||||
|
|
|
@ -17,7 +17,9 @@ class AdminFormMixin(object):
|
|||
def as_admin(self):
|
||||
prepopulated_fields = {}
|
||||
fieldsets = [
|
||||
(None, {'fields': list(self.fields.keys())})
|
||||
(None, {
|
||||
'fields': list(self.fields.keys())
|
||||
}),
|
||||
]
|
||||
adminform = helpers.AdminForm(self, fieldsets, prepopulated_fields)
|
||||
template = Template(
|
||||
|
@ -25,7 +27,10 @@ class AdminFormMixin(object):
|
|||
' {% include "admin/includes/fieldset.html" %}'
|
||||
'{% endfor %}'
|
||||
)
|
||||
return template.render(Context({'adminform': adminform}))
|
||||
context = Context({
|
||||
'adminform': adminform
|
||||
})
|
||||
return template.render(context)
|
||||
|
||||
|
||||
class AdminFormSet(BaseModelFormSet):
|
||||
|
@ -43,7 +48,10 @@ class AdminFormSet(BaseModelFormSet):
|
|||
template = Template(
|
||||
'{% include "admin/edit_inline/tabular.html" %}'
|
||||
)
|
||||
return template.render(Context({'inline_admin_formset': inline_admin_formset}))
|
||||
context = Context({
|
||||
'inline_admin_formset': inline_admin_formset
|
||||
})
|
||||
return template.render(context)
|
||||
|
||||
|
||||
def adminmodelformset_factory(modeladmin, form, formset=AdminFormSet, **kwargs):
|
||||
|
|
|
@ -98,11 +98,13 @@ class ChangeViewActionsMixin(object):
|
|||
action = getattr(self, action)
|
||||
view = action_to_view(action, self)
|
||||
view.url_name = getattr(action, 'url_name', action.__name__)
|
||||
verbose_name = getattr(action, 'verbose_name',
|
||||
view.url_name.capitalize().replace('_', ' '))
|
||||
if hasattr(verbose_name, '__call__'):
|
||||
verbose_name = verbose_name(obj)
|
||||
view.verbose_name = verbose_name
|
||||
tool_description = getattr(action, 'tool_description', '')
|
||||
if not tool_description:
|
||||
tool_description = getattr(action, 'short_description',
|
||||
view.url_name.capitalize().replace('_', ' '))
|
||||
if hasattr(tool_description, '__call__'):
|
||||
tool_description = tool_description(obj)
|
||||
view.tool_description = tool_description
|
||||
view.css_class = getattr(action, 'css_class', 'historylink')
|
||||
view.help_text = getattr(action, 'help_text', '')
|
||||
views.append(view)
|
||||
|
|
|
@ -98,7 +98,7 @@ def change_url(obj):
|
|||
@admin_field
|
||||
def admin_link(*args, **kwargs):
|
||||
instance = args[-1]
|
||||
if kwargs['field'] in ['id', 'pk', '__str__']:
|
||||
if kwargs['field'] in ('id', 'pk', '__str__'):
|
||||
obj = instance
|
||||
else:
|
||||
try:
|
||||
|
@ -120,7 +120,7 @@ def admin_link(*args, **kwargs):
|
|||
extra = ''
|
||||
if kwargs['popup']:
|
||||
extra = 'onclick="return showAddAnotherPopup(this);"'
|
||||
return '<a href="%s" %s>%s</a>' % (url, extra, display)
|
||||
return mark_safe('<a href="%s" %s>%s</a>' % (url, extra, display))
|
||||
|
||||
|
||||
@admin_field
|
||||
|
|
|
@ -25,7 +25,7 @@ def list_contacts(modeladmin, request, queryset):
|
|||
url = reverse('admin:contacts_contact_changelist')
|
||||
url += '?account__in=%s' % ','.join(map(str, ids))
|
||||
return redirect(url)
|
||||
list_contacts.verbose_name = _("List contacts")
|
||||
list_contacts.short_description = _("List contacts")
|
||||
|
||||
|
||||
def service_report(modeladmin, request, queryset):
|
||||
|
|
|
@ -31,7 +31,7 @@ def view_bill(modeladmin, request, queryset):
|
|||
return
|
||||
html = bill.html or bill.render()
|
||||
return HttpResponse(html)
|
||||
view_bill.verbose_name = _("View")
|
||||
view_bill.tool_description = _("View")
|
||||
view_bill.url_name = 'view'
|
||||
|
||||
|
||||
|
@ -91,7 +91,7 @@ def close_bills(modeladmin, request, queryset, action='close_bills'):
|
|||
'obj': get_object_from_url(modeladmin, request),
|
||||
}
|
||||
return render(request, 'admin/orchestra/generic_confirmation.html', context)
|
||||
close_bills.verbose_name = _("Close")
|
||||
close_bills.tool_description = _("Close")
|
||||
close_bills.url_name = 'close'
|
||||
|
||||
|
||||
|
@ -137,7 +137,7 @@ def download_bills(modeladmin, request, queryset):
|
|||
response = HttpResponse(pdf, content_type='application/pdf')
|
||||
response['Content-Disposition'] = 'attachment; filename="%s.pdf"' % bill.number
|
||||
return response
|
||||
download_bills.verbose_name = _("Download")
|
||||
download_bills.tool_description = _("Download")
|
||||
download_bills.url_name = 'download'
|
||||
|
||||
|
||||
|
@ -149,7 +149,7 @@ def close_send_download_bills(modeladmin, request, queryset):
|
|||
return
|
||||
return download_bills(modeladmin, request, queryset)
|
||||
return response
|
||||
close_send_download_bills.verbose_name = _("C.S.D.")
|
||||
close_send_download_bills.tool_description = _("C.S.D.")
|
||||
close_send_download_bills.url_name = 'close-send-download'
|
||||
close_send_download_bills.help_text = _("Close, send and download bills in one shot.")
|
||||
|
||||
|
@ -288,7 +288,7 @@ def amend_bills(modeladmin, request, queryset):
|
|||
_('<a href="%(url)s">%(num)i amendment bills</a> have been generated.') % context,
|
||||
num
|
||||
)))
|
||||
amend_bills.verbose_name = _("Amend")
|
||||
amend_bills.tool_description = _("Amend")
|
||||
amend_bills.url_name = 'amend'
|
||||
|
||||
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
import copy
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.admin import helpers
|
||||
from django.db.models import Q
|
||||
from django.db.models.functions import Concat, Coalesce
|
||||
from django.shortcuts import render
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||
from django.template.response import TemplateResponse
|
||||
|
||||
from orchestra.admin.forms import adminmodelformset_factory
|
||||
from orchestra.admin.utils import get_object_from_url, change_url
|
||||
from orchestra.admin.utils import get_object_from_url, change_url, admin_link
|
||||
from orchestra.utils.python import AttrDict
|
||||
|
||||
from .forms import RecordForm, RecordEditFormSet
|
||||
from .forms import RecordForm, RecordEditFormSet, SOAForm
|
||||
from .models import Record
|
||||
|
||||
|
||||
|
@ -23,15 +26,33 @@ def view_zone(modeladmin, request, queryset):
|
|||
}
|
||||
return TemplateResponse(request, 'admin/domains/domain/view_zone.html', context)
|
||||
view_zone.url_name = 'view-zone'
|
||||
view_zone.verbose_name = _("View zone")
|
||||
view_zone.short_description = _("View zone")
|
||||
|
||||
|
||||
def edit_records(modeladmin, request, queryset):
|
||||
selected_ids = queryset.values_list('id', flat=True)
|
||||
# Include subodmains
|
||||
queryset = queryset.model.objects.filter(
|
||||
Q(top__id__in=selected_ids) | Q(id__in=selected_ids)
|
||||
).annotate(
|
||||
structured_id=Coalesce('top__id', 'id'),
|
||||
structured_name=Concat('top__name', 'name')
|
||||
).order_by('-structured_id', 'structured_name')
|
||||
formsets = []
|
||||
for domain in queryset.prefetch_related('records'):
|
||||
modeladmin_copy = copy.copy(modeladmin)
|
||||
modeladmin_copy.model = Record
|
||||
link = '<a href="%s">%s</a>' % (change_url(domain), domain.name)
|
||||
prefix = '' if domain.is_top else ' '*8
|
||||
context = {
|
||||
'url': change_url(domain),
|
||||
'name': prefix+domain.name,
|
||||
'title': '',
|
||||
}
|
||||
if domain.id not in selected_ids:
|
||||
context['name'] += '*'
|
||||
context['title'] = _("This subdomain was not explicitly selected "
|
||||
"but has been automatically added to this list.")
|
||||
link = '<a href="%(url)s" title="%(title)s">%(name)s</a>' % context
|
||||
modeladmin_copy.verbose_name_plural = mark_safe(link)
|
||||
RecordFormSet = adminmodelformset_factory(
|
||||
modeladmin_copy, RecordForm, formset=RecordEditFormSet, extra=1, can_delete=True)
|
||||
|
@ -73,7 +94,7 @@ def edit_records(modeladmin, request, queryset):
|
|||
opts = modeladmin.model._meta
|
||||
context = {
|
||||
'title': _("Edit records"),
|
||||
'action_name': 'Edit records',
|
||||
'action_name': _("Edit records"),
|
||||
'action_value': 'edit_records',
|
||||
'display_objects': [],
|
||||
'queryset': queryset,
|
||||
|
@ -86,6 +107,46 @@ def edit_records(modeladmin, request, queryset):
|
|||
return render(request, 'admin/domains/domain/edit_records.html', context)
|
||||
|
||||
|
||||
def add_records(modeladmin, request, queryset):
|
||||
# TODO
|
||||
pass
|
||||
def set_soa(modeladmin, request, queryset):
|
||||
if queryset.filter(top__isnull=False).exists():
|
||||
msg = _("Set SOA on subdomains is not possible.")
|
||||
modeladmin.message_user(request, msg, messages.ERROR)
|
||||
return
|
||||
form = SOAForm()
|
||||
if request.POST.get('post') == 'generic_confirmation':
|
||||
form = SOAForm(request.POST)
|
||||
if form.is_valid():
|
||||
updates = {name: value for name, value in form.cleaned_data.items() if value}
|
||||
change_message = _("SOA set %s") % str(updates)[1:-1]
|
||||
for domain in queryset:
|
||||
for name, value in updates.items():
|
||||
if name.startswith('clear_'):
|
||||
name = name.replace('clear_', '')
|
||||
value = ''
|
||||
setattr(domain, name, value)
|
||||
modeladmin.log_change(request, domain, change_message)
|
||||
domain.save()
|
||||
num = len(queryset)
|
||||
msg = ungettext(
|
||||
_("SOA record for one domain has been updated."),
|
||||
_("SOA record for %s domains has been updated.") % num,
|
||||
num
|
||||
)
|
||||
modeladmin.message_user(request, msg)
|
||||
return
|
||||
opts = modeladmin.model._meta
|
||||
context = {
|
||||
'title': _("Set SOA for selected domains"),
|
||||
'content_message': '',
|
||||
'action_name': _("Set SOA"),
|
||||
'action_value': 'set_soa',
|
||||
'display_objects': [admin_link('__str__')(domain) for domain in queryset],
|
||||
'queryset': queryset,
|
||||
'opts': opts,
|
||||
'app_label': opts.app_label,
|
||||
'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
|
||||
'form': form,
|
||||
'obj': get_object_from_url(modeladmin, request),
|
||||
}
|
||||
return render(request, 'admin/orchestra/generic_confirmation.html', context)
|
||||
set_soa.short_description = _("Set SOA for selected domains")
|
||||
|
|
|
@ -8,7 +8,7 @@ from orchestra.admin.utils import admin_link, change_url
|
|||
from orchestra.contrib.accounts.admin import AccountAdminMixin
|
||||
from orchestra.utils import apps
|
||||
|
||||
from .actions import view_zone, edit_records
|
||||
from .actions import view_zone, edit_records, set_soa
|
||||
from .filters import TopDomainListFilter
|
||||
from .forms import RecordForm, RecordInlineFormSet, BatchDomainCreationAdminForm
|
||||
from .models import Domain, Record
|
||||
|
@ -51,13 +51,16 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
|||
)
|
||||
add_fields = ('name', 'account')
|
||||
fields = ('name', 'account_link')
|
||||
inlines = [RecordInline, DomainInline]
|
||||
list_filter = [TopDomainListFilter]
|
||||
readonly_fields = ('account_link', 'top_link',)
|
||||
inlines = (RecordInline, DomainInline)
|
||||
list_filter = (TopDomainListFilter,)
|
||||
change_readonly_fields = ('name', 'serial')
|
||||
search_fields = ('name', 'account__username')
|
||||
add_form = BatchDomainCreationAdminForm
|
||||
actions = (edit_records,)
|
||||
change_view_actions = [view_zone]
|
||||
actions = (edit_records, set_soa)
|
||||
change_view_actions = (view_zone, edit_records)
|
||||
|
||||
top_link = admin_link('top')
|
||||
|
||||
def structured_name(self, domain):
|
||||
if domain.is_top:
|
||||
|
@ -90,15 +93,26 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
|||
def get_fieldsets(self, request, obj=None):
|
||||
""" Add SOA fields when domain is top """
|
||||
fieldsets = super(DomainAdmin, self).get_fieldsets(request, obj)
|
||||
if obj and obj.is_top:
|
||||
fieldsets += (
|
||||
(_("SOA"), {
|
||||
'classes': ('collapse',),
|
||||
'fields': ('serial', 'refresh', 'retry', 'expire', 'min_ttl'),
|
||||
}),
|
||||
)
|
||||
if obj:
|
||||
if obj.is_top:
|
||||
fieldsets += (
|
||||
(_("SOA"), {
|
||||
'classes': ('collapse',),
|
||||
'fields': ('serial', 'refresh', 'retry', 'expire', 'min_ttl'),
|
||||
}),
|
||||
)
|
||||
else:
|
||||
existing = fieldsets[0][1]['fields']
|
||||
if 'top_link' not in existing:
|
||||
fieldsets[0][1]['fields'].insert(2, 'top_link')
|
||||
return fieldsets
|
||||
|
||||
def get_inline_instances(self, request, obj=None):
|
||||
inlines = super(DomainAdmin, self).get_inline_instances(request, obj)
|
||||
if not obj or not obj.is_top:
|
||||
return [inline for inline in inlines if type(inline) != DomainInline]
|
||||
return inlines
|
||||
|
||||
def get_queryset(self, request):
|
||||
""" Order by structured name and imporve performance """
|
||||
qs = super(DomainAdmin, self).get_queryset(request)
|
||||
|
@ -121,12 +135,6 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
|||
domain = Domain.objects.create(name=name, account_id=obj.account_id)
|
||||
self.extra_domains.append(domain)
|
||||
|
||||
def save_formset(self, request, form, formset, change):
|
||||
"""
|
||||
Given an inline formset save it to the database.
|
||||
"""
|
||||
formset.save()
|
||||
|
||||
def save_related(self, request, form, formsets, change):
|
||||
""" batch domain creation support """
|
||||
super(DomainAdmin, self).save_related(request, form, formsets, change)
|
||||
|
@ -142,7 +150,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
|||
record.pk = None
|
||||
formset.instance = domain
|
||||
form.instance = domain
|
||||
self.save_formset(request, form, formset, change=change)
|
||||
self.save_formset(request, form, formset, change)
|
||||
|
||||
|
||||
admin.site.register(Domain, DomainAdmin)
|
||||
|
|
|
@ -174,7 +174,8 @@ class Bind9MasterDomainBackend(ServiceController):
|
|||
class Bind9SlaveDomainBackend(Bind9MasterDomainBackend):
|
||||
"""
|
||||
Generate the configuartion for slave servers
|
||||
It auto-discover the master server based on your routing configuration or you can use DOMAINS_MASTERS to explicitly configure the master.
|
||||
It auto-discover the master server based on your routing configuration or you can use
|
||||
DOMAINS_MASTERS to explicitly configure the master.
|
||||
"""
|
||||
CONF_PATH = settings.DOMAINS_SLAVES_PATH
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
from django import forms
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.text import capfirst
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.admin.forms import AdminFormSet
|
||||
from orchestra.admin.forms import AdminFormSet, AdminFormMixin
|
||||
|
||||
from . import validators
|
||||
from .helpers import domain_for_validation
|
||||
|
@ -71,8 +72,8 @@ class RecordForm(forms.ModelForm):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RecordForm, self).__init__(*args, **kwargs)
|
||||
self.fields['ttl'].widget = forms.TextInput(attrs={'size':'10'})
|
||||
self.fields['value'].widget = forms.TextInput(attrs={'size':'100'})
|
||||
self.fields['ttl'].widget = forms.TextInput(attrs={'size': '10'})
|
||||
self.fields['value'].widget = forms.TextInput(attrs={'size': '100'})
|
||||
|
||||
|
||||
class ValidateZoneMixin(object):
|
||||
|
@ -97,3 +98,30 @@ class RecordEditFormSet(ValidateZoneMixin, AdminFormSet):
|
|||
|
||||
class RecordInlineFormSet(ValidateZoneMixin, forms.models.BaseInlineFormSet):
|
||||
pass
|
||||
|
||||
|
||||
class SOAForm(AdminFormMixin, forms.Form):
|
||||
refresh = forms.CharField()
|
||||
clear_refresh = forms.BooleanField(label=_("Clear refresh"), required=False,
|
||||
help_text=_("Remove custom refresh value for all selected domains."))
|
||||
retry = forms.CharField()
|
||||
clear_retry = forms.BooleanField(label=_("Clear retry"), required=False,
|
||||
help_text=_("Remove custom retry value for all selected domains."))
|
||||
expire = forms.CharField()
|
||||
clear_expire = forms.BooleanField(label=_("Clear expire"), required=False,
|
||||
help_text=_("Remove custom expire value for all selected domains."))
|
||||
min_ttl = forms.CharField()
|
||||
clear_min_ttl = forms.BooleanField(label=_("Clear min TTL"), required=False,
|
||||
help_text=_("Remove custom min TTL value for all selected domains."))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SOAForm, self).__init__(*args, **kwargs)
|
||||
for name in self.fields:
|
||||
if not name.startswith('clear_'):
|
||||
field = Domain._meta.get_field_by_name(name)[0]
|
||||
self.fields[name] = forms.CharField(
|
||||
label=capfirst(field.verbose_name),
|
||||
help_text=field.help_text,
|
||||
validators=field.validators,
|
||||
required=False,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import orchestra.contrib.domains.utils
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('domains', '0002_auto_20150715_1017'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='domain',
|
||||
name='expire',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='domain',
|
||||
name='min_ttl',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='domain',
|
||||
name='refresh',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='domain',
|
||||
name='retry',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='domain',
|
||||
name='serial',
|
||||
field=models.IntegerField(editable=False, verbose_name='serial', default=orchestra.contrib.domains.utils.generate_zone_serial, help_text='A revision number that changes whenever this domain is updated.'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,35 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import orchestra.contrib.domains.validators
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('domains', '0003_auto_20150720_1121'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='domain',
|
||||
name='expire',
|
||||
field=models.CharField(validators=[orchestra.contrib.domains.validators.validate_zone_interval], blank=True, help_text='The time that a secondary server will keep trying to complete a zone transfer. If this time expires prior to a successful zone transfer, the secondary server will expire its zone file. This means the secondary will stop answering queries. The default value is <tt>4w</tt>.', verbose_name='expire', max_length=16),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='domain',
|
||||
name='min_ttl',
|
||||
field=models.CharField(validators=[orchestra.contrib.domains.validators.validate_zone_interval], blank=True, help_text='The minimum time-to-live value applies to all resource records in the zone file. This value is supplied in query responses to inform other servers how long they should keep the data in cache. The default value is <tt>1h</tt>.', verbose_name='min TTL', max_length=16),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='domain',
|
||||
name='refresh',
|
||||
field=models.CharField(validators=[orchestra.contrib.domains.validators.validate_zone_interval], blank=True, help_text="The time a secondary DNS server waits before querying the primary DNS server's SOA record to check for changes. When the refresh time expires, the secondary DNS server requests a copy of the current SOA record from the primary. The primary DNS server complies with this request. The secondary DNS server compares the serial number of the primary DNS server's current SOA record and the serial number in it's own SOA record. If they are different, the secondary DNS server will request a zone transfer from the primary DNS server. The default value is <tt>1d</tt>.", verbose_name='refresh', max_length=16),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='domain',
|
||||
name='retry',
|
||||
field=models.CharField(validators=[orchestra.contrib.domains.validators.validate_zone_interval], blank=True, help_text='The time a secondary server waits before retrying a failed zone transfer. Normally, the retry time is less than the refresh time. The default value is <tt>2h</tt>.', verbose_name='retry', max_length=16),
|
||||
),
|
||||
]
|
|
@ -21,7 +21,7 @@ class Domain(models.Model):
|
|||
editable=False)
|
||||
serial = models.IntegerField(_("serial"), default=utils.generate_zone_serial, editable=False,
|
||||
help_text=_("A revision number that changes whenever this domain is updated."))
|
||||
refresh = models.IntegerField(_("refresh"), null=True, blank=True,
|
||||
refresh = models.CharField(_("refresh"), max_length=16, blank=True,
|
||||
validators=[validators.validate_zone_interval],
|
||||
help_text=_("The time a secondary DNS server waits before querying the primary DNS "
|
||||
"server's SOA record to check for changes. When the refresh time expires, "
|
||||
|
@ -32,19 +32,19 @@ class Domain(models.Model):
|
|||
"If they are different, the secondary DNS server will request a zone "
|
||||
"transfer from the primary DNS server. "
|
||||
"The default value is <tt>%s</tt>.") % settings.DOMAINS_DEFAULT_REFRESH)
|
||||
retry = models.IntegerField(_("retry"), null=True, blank=True,
|
||||
retry = models.CharField(_("retry"), max_length=16, blank=True,
|
||||
validators=[validators.validate_zone_interval],
|
||||
help_text=_("The time a secondary server waits before retrying a failed zone transfer. "
|
||||
"Normally, the retry time is less than the refresh time. "
|
||||
"The default value is <tt>%s</tt>.") % settings.DOMAINS_DEFAULT_RETRY)
|
||||
expire = models.IntegerField(_("expire"), null=True, blank=True,
|
||||
expire = models.CharField(_("expire"), max_length=16, blank=True,
|
||||
validators=[validators.validate_zone_interval],
|
||||
help_text=_("The time that a secondary server will keep trying to complete a zone "
|
||||
"transfer. If this time expires prior to a successful zone transfer, "
|
||||
"the secondary server will expire its zone file. This means the secondary "
|
||||
"will stop answering queries. "
|
||||
"The default value is <tt>%s</tt>.") % settings.DOMAINS_DEFAULT_EXPIRE)
|
||||
min_ttl = models.IntegerField(_("min TTL"), null=True, blank=True,
|
||||
min_ttl = models.CharField(_("min TTL"), max_length=16, blank=True,
|
||||
validators=[validators.validate_zone_interval],
|
||||
help_text=_("The minimum time-to-live value applies to all resource records in the "
|
||||
"zone file. This value is supplied in query responses to inform other "
|
||||
|
|
|
@ -68,7 +68,7 @@ def mark_as_executed(modeladmin, request, queryset):
|
|||
num)
|
||||
modeladmin.message_user(request, msg)
|
||||
mark_as_executed.url_name = 'execute'
|
||||
mark_as_executed.verbose_name = _("Mark as executed")
|
||||
mark_as_executed.short_description = _("Mark as executed")
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
|
@ -84,7 +84,7 @@ def mark_as_secured(modeladmin, request, queryset):
|
|||
num)
|
||||
modeladmin.message_user(request, msg)
|
||||
mark_as_secured.url_name = 'secure'
|
||||
mark_as_secured.verbose_name = _("Mark as secured")
|
||||
mark_as_secured.short_description = _("Mark as secured")
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
|
@ -100,7 +100,7 @@ def mark_as_rejected(modeladmin, request, queryset):
|
|||
num)
|
||||
modeladmin.message_user(request, msg)
|
||||
mark_as_rejected.url_name = 'reject'
|
||||
mark_as_rejected.verbose_name = _("Mark as rejected")
|
||||
mark_as_rejected.short_description = _("Mark as rejected")
|
||||
|
||||
|
||||
def _format_display_objects(modeladmin, request, queryset, related):
|
||||
|
@ -139,7 +139,7 @@ def mark_process_as_executed(modeladmin, request, queryset):
|
|||
num)
|
||||
modeladmin.message_user(request, msg)
|
||||
mark_process_as_executed.url_name = 'executed'
|
||||
mark_process_as_executed.verbose_name = _("Mark as executed")
|
||||
mark_process_as_executed.short_description = _("Mark as executed")
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
|
@ -155,7 +155,7 @@ def abort(modeladmin, request, queryset):
|
|||
num)
|
||||
modeladmin.message_user(request, msg)
|
||||
abort.url_name = 'abort'
|
||||
abort.verbose_name = _("Abort")
|
||||
abort.short_description = _("Abort")
|
||||
|
||||
|
||||
@transaction.atomic
|
||||
|
@ -171,7 +171,7 @@ def commit(modeladmin, request, queryset):
|
|||
num)
|
||||
modeladmin.message_user(request, msg)
|
||||
commit.url_name = 'commit'
|
||||
commit.verbose_name = _("Commit")
|
||||
commit.short_description = _("Commit")
|
||||
|
||||
|
||||
def delete_selected(modeladmin, request, queryset):
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import hashlib
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
import requests
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.contrib.orchestration import ServiceController
|
||||
from orchestra.utils.sys import sshrun
|
||||
|
||||
from .. import settings
|
||||
|
||||
|
@ -45,7 +48,28 @@ class PhpListSaaSBackend(ServiceController):
|
|||
if response.status_code != 200:
|
||||
raise RuntimeError("Bad status code %i" % response.status_code)
|
||||
else:
|
||||
raise NotImplementedError("Change password not implemented")
|
||||
md5_password = hashlib.md5()
|
||||
md5_password.update(saas.password.encode('ascii'))
|
||||
context = {
|
||||
'name': saas.name,
|
||||
'site_name': saas.name,
|
||||
'db_user': settings.SAAS_PHPLIST_DB_USER,
|
||||
'db_pass': settings.SAAS_PHPLIST_DB_PASS,
|
||||
'db_name': settings.SAAS_PHPLIST_DB_NAME,
|
||||
'db_host': settings.SAAS_PHPLIST_DB_HOST,
|
||||
'digest': md5_password.hexdigest(),
|
||||
}
|
||||
context['db_name'] = context['db_name'] % context
|
||||
cmd = textwrap.dedent("""\
|
||||
mysql \\
|
||||
--host=%(db_host)s \\
|
||||
--user=%(db_user)s \\
|
||||
--password=%(db_pass)s \\
|
||||
--execute='UPDATE phplist_admin SET password="%(digest)s" where ID=1; \\
|
||||
UPDATE phplist_user_user SET password="%(digest)s" where ID=1;' \\
|
||||
%(db_name)s""") % context
|
||||
print(cmd)
|
||||
sshrun(server.get_address(), cmd)
|
||||
|
||||
def save(self, saas):
|
||||
if hasattr(saas, 'password'):
|
||||
|
|
|
@ -54,7 +54,7 @@ class PHPListService(SoftwareService):
|
|||
return db_name[:65]
|
||||
|
||||
def get_db_user(self):
|
||||
return settings.SAAS_PHPLIST_DB_NAME
|
||||
return settings.SAAS_PHPLIST_DB_USER
|
||||
|
||||
def get_account(self):
|
||||
return self.instance.account.get_main()
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from orchestra.contrib.settings import Setting
|
||||
from orchestra.settings import ORCHESTRA_BASE_DOMAIN
|
||||
|
||||
|
@ -76,10 +78,30 @@ SAAS_DRUPAL_SITES_PATH = Setting('WEBSITES_DRUPAL_SITES_PATH',
|
|||
)
|
||||
|
||||
|
||||
SAAS_PHPLIST_DB_NAME = Setting('SAAS_PHPLIST_DB_NAME',
|
||||
SAAS_PHPLIST_DB_USER = Setting('SAAS_PHPLIST_DB_USER',
|
||||
'phplist_mu',
|
||||
help_text=_("Needed for password changing support."),
|
||||
)
|
||||
|
||||
|
||||
SAAS_PHPLIST_DB_PASS = Setting('SAAS_PHPLIST_DB_PASS',
|
||||
'secret',
|
||||
help_text=_("Needed for password changing support."),
|
||||
)
|
||||
|
||||
|
||||
SAAS_PHPLIST_DB_NAME = Setting('SAAS_PHPLIST_DB_NAME',
|
||||
'phplist_mu_%(site_name)s',
|
||||
help_text=_("Needed for password changing support."),
|
||||
)
|
||||
|
||||
|
||||
SAAS_PHPLIST_DB_HOST = Setting('SAAS_PHPLIST_DB_HOST',
|
||||
'loclahost',
|
||||
help_text=_("Needed for password changing support."),
|
||||
)
|
||||
|
||||
|
||||
SAAS_PHPLIST_BASE_DOMAIN = Setting('SAAS_PHPLIST_BASE_DOMAIN',
|
||||
'lists.{}'.format(ORCHESTRA_BASE_DOMAIN),
|
||||
help_text="Uses <tt>ORCHESTRA_BASE_DOMAIN</tt> by default.",
|
||||
|
|
|
@ -47,7 +47,7 @@ def update_orders(modeladmin, request, queryset, extra_context=None):
|
|||
}
|
||||
return render(request, 'admin/services/service/update_orders.html', context)
|
||||
update_orders.url_name = 'update-orders'
|
||||
update_orders.verbose_name = _("Update orders")
|
||||
update_orders.short_description = _("Update orders")
|
||||
|
||||
|
||||
def view_help(modeladmin, request, queryset):
|
||||
|
@ -62,7 +62,7 @@ def view_help(modeladmin, request, queryset):
|
|||
}
|
||||
return TemplateResponse(request, 'admin/services/service/help.html', context)
|
||||
view_help.url_name = 'help'
|
||||
view_help.verbose_name = _("Help")
|
||||
view_help.tool_description = _("Help")
|
||||
|
||||
|
||||
def clone(modeladmin, request, queryset):
|
||||
|
|
|
@ -67,7 +67,7 @@ def set_permission(modeladmin, request, queryset):
|
|||
return TemplateResponse(request, 'admin/systemusers/systemuser/set_permission.html',
|
||||
context, current_app=modeladmin.admin_site.name)
|
||||
set_permission.url_name = 'set-permission'
|
||||
set_permission.verbose_name = _("Set permission")
|
||||
set_permission.tool_description = _("Set permission")
|
||||
|
||||
|
||||
def delete_selected(modeladmin, request, queryset):
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
{% block object-tools-items %}
|
||||
{% for item in object_tools_items %}
|
||||
<li><a href="{{ item.url_name }}/" class="{{ item.css_class }}" title="{{ item.help_text }}">{{ item.verbose_name }}</a></li>
|
||||
<li><a href="{{ item.url_name }}/" class="{{ item.css_class }}" title="{{ item.help_text }}">{{ item.tool_description }}</a></li>
|
||||
{% endfor %}
|
||||
{{ block.super }}
|
||||
{% endblock %}
|
||||
|
|
Loading…
Reference in a new issue