Added list accounts changelist actions and display HTML emails on mailer

This commit is contained in:
Marc Aymerich 2015-07-21 10:44:32 +00:00
parent 708758880d
commit 7d1e4d1451
21 changed files with 138 additions and 37 deletions

View file

@ -427,3 +427,5 @@ Case
# bill changelist: dates: (closed_on, created_on, updated_on)
# Resource data inline show info: link to monitor data, and history chart: link to monitor data of each item
# DIsplay message email content nicelly

View file

@ -18,7 +18,7 @@ from . import settings
def list_contacts(modeladmin, request, queryset):
ids = queryset.values_list('id', flat=True)
ids = queryset.order_by().values_list('id', flat=True).distinct()
if not ids:
messages.warning(request, "Select at least one account.")
return
@ -28,6 +28,17 @@ def list_contacts(modeladmin, request, queryset):
list_contacts.short_description = _("List contacts")
def list_accounts(modeladmin, request, queryset):
accounts = queryset.order_by().values_list('account_id', flat=True).distinct()
if not accounts:
messages.warning(request, "Select at least one instance.")
return
url = reverse('admin:contacts_contact_changelist')
url += '?id__in=%s' % ','.join(map(str, accounts))
return redirect(url)
list_accounts.short_description = _("List accounts")
def service_report(modeladmin, request, queryset):
# TODO resources
accounts = []

View file

@ -13,6 +13,7 @@ from django.shortcuts import redirect
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import admin_date, insertattr, admin_link
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin, AccountAdmin
from orchestra.forms.widgets import paddingCheckboxSelectMultiple
@ -212,7 +213,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
actions = [
actions.manage_lines, actions.download_bills, actions.close_bills, actions.send_bills,
actions.amend_bills, actions.bill_report, actions.service_report,
actions.close_send_download_bills,
actions.close_send_download_bills, list_accounts,
]
change_readonly_fields = ('account_link', 'type', 'is_open', 'amend_of_link', 'amend_links')
readonly_fields = ('number', 'display_total', 'is_sent', 'display_payment_state')

View file

@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import AtLeastOneRequiredInlineFormSet, ExtendedModelAdmin
from orchestra.admin.actions import SendEmail
from orchestra.admin.utils import insertattr, change_url
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdmin, AccountAdminMixin
from orchestra.forms.widgets import paddingCheckboxSelectMultiple
@ -59,7 +60,7 @@ class ContactAdmin(AccountAdminMixin, ExtendedModelAdmin):
'fields': ('address', ('zipcode', 'city'), 'country')
}),
)
actions = [SendEmail(),]
actions = (SendEmail(), list_accounts)
def dispaly_name(self, contact):
return str(contact)

View file

@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.utils import change_url
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
from .forms import DatabaseCreationForm, DatabaseUserChangeForm, DatabaseUserCreationForm
@ -42,6 +43,7 @@ class DatabaseAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
filter_horizontal = ['users']
filter_by_account_fields = ('users',)
list_prefetch_related = ('users',)
actions = (list_accounts,)
def display_users(self, db):
links = []
@ -90,6 +92,7 @@ class DatabaseUserAdmin(SelectAccountAdminMixin, ChangePasswordAdminMixin, Exten
readonly_fields = ('account_link', 'display_databases',)
filter_by_account_fields = ('databases',)
list_prefetch_related = ('databases',)
actions = (list_accounts,)
def display_databases(self, user):
links = []

View file

@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import admin_link, change_url
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin
from orchestra.utils import apps
@ -57,7 +58,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
change_readonly_fields = ('name', 'serial')
search_fields = ('name', 'account__username')
add_form = BatchDomainCreationAdminForm
actions = (edit_records, set_soa)
actions = (edit_records, set_soa, list_accounts)
change_view_actions = (view_zone, edit_records)
top_link = admin_link('top')

View file

@ -80,15 +80,15 @@ class ValidateZoneMixin(object):
def clean(self):
""" Checks if everything is consistent """
super(ValidateZoneMixin, self).clean()
if any(formset.errors):
if any(self.errors):
return
if formset.instance.name:
if self.instance.name:
records = []
for form in formset.forms:
for form in self.forms:
data = form.cleaned_data
if data and not data['DELETE']:
records.append(data)
domain = domain_for_validation(formset.instance, records)
domain = domain_for_validation(self.instance, records)
validators.validate_zone(domain.render_zone())

View file

@ -273,14 +273,14 @@ class Record(models.Model):
)
VALIDATORS = {
MX: validators.validate_mx_record,
NS: validators.validate_zone_label,
A: validate_ipv4_address,
AAAA: validate_ipv6_address,
CNAME: validators.validate_zone_label,
TXT: validate_ascii,
SRV: validators.validate_srv_record,
SOA: validators.validate_soa_record,
MX: (validators.validate_mx_record,),
NS: (validators.validate_zone_label,),
A: (validate_ipv4_address,),
AAAA: (validate_ipv6_address,),
CNAME: (validators.validate_zone_label,),
TXT: (validate_ascii, validators.validate_quoted_record),
SRV: (validators.validate_srv_record,),
SOA: (validators.validate_soa_record,),
}
domain = models.ForeignKey(Domain, verbose_name=_("domain"), related_name='records')
@ -300,12 +300,13 @@ class Record(models.Model):
if self.type != self.TXT:
self.value = self.value.lower().strip()
if self.type:
try:
self.VALIDATORS[self.type](self.value)
except ValidationError as error:
raise ValidationError({
'value': error,
})
for validator in self.VALIDATORS.get(self.type, []):
try:
validator(self.value)
except ValidationError as error:
raise ValidationError({
'value': error,
})
def get_ttl(self):
return self.ttl or settings.DOMAINS_DEFAULT_TTL

View file

@ -107,6 +107,16 @@ def validate_soa_record(value):
raise ValidationError(msg)
def validate_quoted_record(value):
value = value.strip()
if ' ' in value and (value[0] != '"' or value[-1] != '"'):
raise ValidationError(
_("This record value contains spaces, you must enclose the string in double quotes; "
"otherwise, individual words will be separately quoted and break up the record "
"into multiple parts.")
)
def validate_zone(zone):
""" Ultimate zone file validation using named-checkzone """
zone_name = zone.split()[0][:-1]

View file

@ -6,6 +6,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import disable
from orchestra.admin.utils import admin_link
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
from orchestra.contrib.accounts.filters import IsActiveListFilter
@ -53,7 +54,7 @@ class ListAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModel
add_form = ListCreationForm
list_select_related = ('account', 'address_domain',)
filter_by_account_fields = ['address_domain']
actions = (disable,)
actions = (disable, list_accounts)
address_domain_link = admin_link('address_domain', order='address_domain__name')

View file

@ -10,6 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import disable
from orchestra.admin.utils import admin_link, change_url
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
from orchestra.contrib.accounts.filters import IsActiveListFilter
@ -69,7 +70,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
add_form = MailboxCreationForm
form = MailboxChangeForm
list_prefetch_related = ('addresses__domain',)
actions = (disable,)
actions = (disable, list_accounts)
def __init__(self, *args, **kwargs):
super(MailboxAdmin, self).__init__(*args, **kwargs)

View file

@ -1,3 +1,6 @@
import base64
import email
from django import forms
from django.contrib import admin
from django.core.urlresolvers import reverse
@ -12,7 +15,7 @@ from .engine import send_pending
from .models import Message, SMTPLog
COLORS = {
COLORS = {
Message.QUEUED: 'purple',
Message.SENT: 'green',
Message.DEFERRED: 'darkorange',
@ -29,6 +32,21 @@ class MessageAdmin(admin.ModelAdmin):
)
list_filter = ('state', 'priority', 'retries')
list_prefetch_related = ('logs__id')
fieldsets = (
(None, {
'fields': ('state', 'priority', ('retries', 'last_retry_delta'),
'display_full_subject', 'display_to_address', 'display_from_address',
'display_content'),
}),
(_("Edit"), {
'classes': ('collapse',),
'fields': ('subject', 'to_address', 'from_address', 'content'),
}),
)
readonly_fields = (
'retries', 'last_retry_delta', 'display_full_subject', 'display_to_address',
'display_from_address', 'display_content',
)
colored_state = admin_colored('state', colors=COLORS)
created_at_delta = admin_date('created_at')
@ -52,6 +70,37 @@ class MessageAdmin(admin.ModelAdmin):
num_logs.admin_order_field = 'logs__count'
num_logs.allow_tags = True
def display_content(self, instance):
part = email.message_from_string(instance.content)
payload = part.get_payload()
if isinstance(payload, list):
for part in payload:
payload = part.get_payload()
if part.get_content_type() == 'text/html':
# prioritize HTML
payload = '<div style="padding-left:110px">%s</div>' % payload
break
if part.get('Content-Transfer-Encoding') == 'base64':
payload = base64.b64decode(payload)
payload = payload.decode(part.get_charsets()[0])
if part.get_content_type() == 'text/plain':
payload = payload.replace('\n', '<br>')
return payload
display_content.short_description = _("Content")
display_content.allow_tags = True
def display_full_subject(self, instance):
return instance.subject
display_full_subject.short_description = _("Subject")
def display_from_address(self, instance):
return instance.from_address
display_from_address.short_description = _("From address")
def display_to_address(self, instance):
return instance.to_address
display_to_address.short_description = _("To address")
def get_urls(self):
from django.conf.urls import url
urls = super(MessageAdmin, self).get_urls()
@ -77,6 +126,7 @@ class MessageAdmin(admin.ModelAdmin):
kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
return super(MessageAdmin, self).formfield_for_dbfield(db_field, **kwargs)
class SMTPLogAdmin(admin.ModelAdmin):
list_display = (
'id', 'message_link', 'colored_result', 'date_delta', 'log_message'

View file

@ -8,6 +8,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import admin_link, admin_date, change_url
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin
from orchestra.utils.humanize import naturaldate
@ -49,13 +50,18 @@ class MetricStorageInline(admin.TabularInline):
class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
list_display = (
'id', 'service_link', 'account_link', 'content_object_link',
'display_registered_on', 'display_billed_until', 'display_cancelled_on', 'display_metric'
'display_registered_on', 'display_billed_until', 'display_cancelled_on',
'display_metric'
)
list_filter = (
ActiveOrderListFilter, IgnoreOrderListFilter, BilledOrderListFilter, 'service'
)
list_filter = (ActiveOrderListFilter, IgnoreOrderListFilter, BilledOrderListFilter, 'service')
default_changelist_filters = (
('ignore', '0'),
)
actions = (BillSelectedOrders(), mark_as_ignored, mark_as_not_ignored, report)
actions = (
BillSelectedOrders(), mark_as_ignored, mark_as_not_ignored, report, list_accounts
)
change_view_actions = (BillSelectedOrders(), mark_as_ignored, mark_as_not_ignored)
date_hierarchy = 'registered_on'
inlines = (MetricStorageInline,)
@ -111,7 +117,9 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
display_billed_until.admin_order_field = 'billed_until'
def display_metric(self, order):
""" dispalys latest metric value, don't uses latest() because not loosing prefetch_related """
"""
dispalys latest metric value, don't uses latest() because not loosing prefetch_related
"""
try:
metric = order.metrics.all()[0]
except IndexError:

View file

@ -5,6 +5,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ChangeViewActionsMixin, ExtendedModelAdmin
from orchestra.admin.utils import admin_colored, admin_link
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
from orchestra.plugins.admin import SelectPluginAdminMixin
@ -91,7 +92,7 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
actions.process_transactions, actions.mark_as_executed, actions.mark_as_secured,
actions.mark_as_rejected,
)
actions = change_view_actions + (actions.report,)
actions = change_view_actions + (actions.report, list_accounts)
filter_by_account_fields = ('bill', 'source')
change_readonly_fields = ('amount', 'currency')
readonly_fields = (

View file

@ -2,6 +2,7 @@ from django.contrib import admin
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import insertattr
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin
from orchestra.contrib.services.models import Service
@ -29,6 +30,7 @@ class ContractedPlanAdmin(AccountAdminMixin, admin.ModelAdmin):
list_filter = ('plan__name',)
list_select_related = ('plan', 'account')
search_fields = ('account__username', 'plan__name', 'id')
actions = (list_accounts,)
admin.site.register(Plan, PlanAdmin)

View file

@ -3,6 +3,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import disable
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin
from orchestra.plugins.admin import SelectPluginAdminMixin
@ -18,7 +19,7 @@ class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMi
plugin = SoftwareService
plugin_field = 'service'
plugin_title = 'Software as a Service'
actions = (disable,)
actions = (disable, list_accounts)
def display_site_domain(self, saas):
site_domain = saas.get_site_domain()

View file

@ -14,7 +14,8 @@ from .. import settings
class PhpListSaaSBackend(ServiceController):
"""
Creates a new phplist instance on a phpList multisite installation.
The site is created by means of creating a new database per phpList site, but all sites share the same code.
The site is created by means of creating a new database per phpList site,
but all sites share the same code.
<tt>// config/config.php
$site = array_shift((explode(".",$_SERVER['HTTP_HOST'])));
@ -28,7 +29,8 @@ class PhpListSaaSBackend(ServiceController):
def _save(self, saas, server):
admin_link = 'https://%s/admin/' % saas.get_site_domain()
print('admin_link:', admin_link)
admin_content = requests.get(admin_link, verify=settings.SAAS_PHPLIST_VERIFY_SSL).content.decode('utf8')
admin_content = requests.get(admin_link, verify=settings.SAAS_PHPLIST_VERIFY_SSL)
admin_content = admin_content.content.decode('utf8')
if admin_content.startswith('Cannot connect to Database'):
raise RuntimeError("Database is not yet configured")
install = re.search(r'([^"]+firstinstall[^"]+)', admin_content)
@ -68,7 +70,7 @@ class PhpListSaaSBackend(ServiceController):
--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)
print('cmd:', cmd)
sshrun(server.get_address(), cmd)
def save(self, saas):

View file

@ -3,6 +3,7 @@ from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
from orchestra.admin.actions import disable
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
from orchestra.contrib.accounts.filters import IsActiveListFilter
@ -42,7 +43,7 @@ class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, Extende
form = SystemUserChangeForm
ordering = ('-id',)
change_view_actions = (set_permission, disable)
actions = (delete_selected,) + change_view_actions
actions = (delete_selected, list_accounts) + change_view_actions
def display_main(self, user):
return user.is_main

View file

@ -4,6 +4,7 @@ from django.contrib.auth.admin import UserAdmin
from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin
from .forms import VPSChangeForm, VPSCreationForm
@ -37,6 +38,7 @@ class VPSAdmin(AccountAdminMixin, ExtendedModelAdmin):
'fields': ('password1', 'password2',)
}),
)
actions = (list_accounts,)
def get_urls(self):
useradmin = UserAdmin(VPS, self.admin_site)

View file

@ -6,6 +6,7 @@ from django.utils.translation import ugettext, ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.utils import change_url, get_modeladmin
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin
from orchestra.forms.widgets import DynamicHelpTextSelect
from orchestra.plugins.admin import SelectPluginAdminMixin
@ -58,6 +59,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
plugin = AppType
plugin_field = 'type'
plugin_title = _("Web application type")
actions = (list_accounts,)
def display_websites(self, webapp):
websites = []

View file

@ -5,10 +5,10 @@ from django.db.models import Q
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
from orchestra.admin import ExtendedModelAdmin
from orchestra.admin.actions import disable
from orchestra.admin.utils import admin_link, change_url
from orchestra.contrib.accounts.actions import list_accounts
from orchestra.contrib.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
from orchestra.forms.widgets import DynamicHelpTextSelect
@ -69,7 +69,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
filter_by_account_fields = ['domains']
list_prefetch_related = ('domains', 'content_set__webapp')
search_fields = ('name', 'account__username', 'domains__name', 'content__webapp__name')
actions = (disable,)
actions = (disable, list_accounts)
def display_domains(self, website):
domains = []