parent
2b06652a5b
commit
eadc06d4c5
|
@ -3,7 +3,7 @@ from collections import OrderedDict
|
||||||
from functools import update_wrapper
|
from functools import update_wrapper
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -56,7 +56,7 @@ def search(request):
|
||||||
if service.search:
|
if service.search:
|
||||||
models.add(service.model)
|
models.add(service.model)
|
||||||
model_name_map[service.model._meta.model_name] = service.model
|
model_name_map[service.model._meta.model_name] = service.model
|
||||||
|
|
||||||
# Account direct access
|
# Account direct access
|
||||||
if search_term.endswith('!'):
|
if search_term.endswith('!'):
|
||||||
from ..contrib.accounts.models import Account
|
from ..contrib.accounts.models import Account
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from fluent_dashboard import dashboard, appsettings
|
from fluent_dashboard import dashboard, appsettings
|
||||||
from fluent_dashboard.modules import CmsAppIconList
|
from fluent_dashboard.modules import CmsAppIconList
|
||||||
|
@ -11,7 +11,7 @@ class AppDefaultIconList(CmsAppIconList):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.icons = kwargs.pop('icons')
|
self.icons = kwargs.pop('icons')
|
||||||
super(AppDefaultIconList, self).__init__(*args, **kwargs)
|
super(AppDefaultIconList, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
def get_icon_for_model(self, app_name, model_name, default=None):
|
def get_icon_for_model(self, app_name, model_name, default=None):
|
||||||
icon = self.icons.get('.'.join((app_name, model_name)))
|
icon = self.icons.get('.'.join((app_name, model_name)))
|
||||||
return super(AppDefaultIconList, self).get_icon_for_model(app_name, model_name, default=icon)
|
return super(AppDefaultIconList, self).get_icon_for_model(app_name, model_name, default=icon)
|
||||||
|
@ -19,7 +19,7 @@ class AppDefaultIconList(CmsAppIconList):
|
||||||
|
|
||||||
class OrchestraIndexDashboard(dashboard.FluentIndexDashboard):
|
class OrchestraIndexDashboard(dashboard.FluentIndexDashboard):
|
||||||
""" Gets application modules from services, accounts and administration registries """
|
""" Gets application modules from services, accounts and administration registries """
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super(dashboard.FluentIndexDashboard, self).__init__(**kwargs)
|
super(dashboard.FluentIndexDashboard, self).__init__(**kwargs)
|
||||||
self.children.append(self.get_personal_module())
|
self.children.append(self.get_personal_module())
|
||||||
|
@ -27,7 +27,7 @@ class OrchestraIndexDashboard(dashboard.FluentIndexDashboard):
|
||||||
recent_actions = self.get_recent_actions_module()
|
recent_actions = self.get_recent_actions_module()
|
||||||
recent_actions.enabled = True
|
recent_actions.enabled = True
|
||||||
self.children.append(recent_actions)
|
self.children.append(recent_actions)
|
||||||
|
|
||||||
def process_registered_view(self, module, view_name, options):
|
def process_registered_view(self, module, view_name, options):
|
||||||
app_name, name = view_name.split('_')[:-1]
|
app_name, name = view_name.split('_')[:-1]
|
||||||
module.icons['.'.join((app_name, name))] = options.get('icon')
|
module.icons['.'.join((app_name, name))] = options.get('icon')
|
||||||
|
@ -47,7 +47,7 @@ class OrchestraIndexDashboard(dashboard.FluentIndexDashboard):
|
||||||
'title': options.get('verbose_name_plural'),
|
'title': options.get('verbose_name_plural'),
|
||||||
'url': add_url,
|
'url': add_url,
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_application_modules(self):
|
def get_application_modules(self):
|
||||||
modules = []
|
modules = []
|
||||||
# Honor settings override, hacky. I Know
|
# Honor settings override, hacky. I Know
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
from admin_tools.menu import items, Menu
|
from admin_tools.menu import items, Menu
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ def api_link(context):
|
||||||
opts = context['cl'].opts
|
opts = context['cl'].opts
|
||||||
else:
|
else:
|
||||||
return reverse('api-root')
|
return reverse('api-root')
|
||||||
if 'object_id' in context:
|
if 'object_id' in context:
|
||||||
object_id = context['object_id']
|
object_id = context['object_id']
|
||||||
try:
|
try:
|
||||||
return reverse('%s-detail' % opts.model_name, args=[object_id])
|
return reverse('%s-detail' % opts.model_name, args=[object_id])
|
||||||
|
@ -42,7 +42,7 @@ def process_registry(register):
|
||||||
item = items.MenuItem(name, url)
|
item = items.MenuItem(name, url)
|
||||||
item.options = options
|
item.options = options
|
||||||
return item
|
return item
|
||||||
|
|
||||||
childrens = {}
|
childrens = {}
|
||||||
for model, options in register.get().items():
|
for model, options in register.get().items():
|
||||||
if options.get('menu', True):
|
if options.get('menu', True):
|
||||||
|
@ -68,7 +68,7 @@ def process_registry(register):
|
||||||
|
|
||||||
class OrchestraMenu(Menu):
|
class OrchestraMenu(Menu):
|
||||||
template = 'admin/orchestra/menu.html'
|
template = 'admin/orchestra/menu.html'
|
||||||
|
|
||||||
def init_with_context(self, context):
|
def init_with_context(self, context):
|
||||||
self.children = [
|
self.children = [
|
||||||
# items.MenuItem(
|
# items.MenuItem(
|
||||||
|
|
|
@ -6,7 +6,7 @@ from functools import wraps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.core.urlresolvers import NoReverseMatch
|
from django.urls import NoReverseMatch
|
||||||
from rest_framework.reverse import reverse
|
from rest_framework.reverse import reverse
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.contrib import messages
|
||||||
from django.contrib.admin import helpers
|
from django.contrib.admin import helpers
|
||||||
from django.contrib.admin.utils import NestedObjects, quote
|
from django.contrib.admin.utils import NestedObjects, quote
|
||||||
from django.contrib.auth import get_permission_codename
|
from django.contrib.auth import get_permission_codename
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.db import router
|
from django.db import router
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
@ -53,14 +53,14 @@ def service_report(modeladmin, request, queryset):
|
||||||
fields.append((model, name))
|
fields.append((model, name))
|
||||||
fields = sorted(fields, key=lambda f: f[0]._meta.verbose_name_plural.lower())
|
fields = sorted(fields, key=lambda f: f[0]._meta.verbose_name_plural.lower())
|
||||||
fields = [field for model, field in fields]
|
fields = [field for model, field in fields]
|
||||||
|
|
||||||
for account in queryset.prefetch_related(*fields):
|
for account in queryset.prefetch_related(*fields):
|
||||||
items = []
|
items = []
|
||||||
for field in fields:
|
for field in fields:
|
||||||
related_manager = getattr(account, field)
|
related_manager = getattr(account, field)
|
||||||
items.append((related_manager.model._meta, related_manager.all()))
|
items.append((related_manager.model._meta, related_manager.all()))
|
||||||
accounts.append((account, items))
|
accounts.append((account, items))
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
'accounts': accounts,
|
'accounts': accounts,
|
||||||
'date': timezone.now().today()
|
'date': timezone.now().today()
|
||||||
|
@ -71,21 +71,21 @@ def service_report(modeladmin, request, queryset):
|
||||||
def delete_related_services(modeladmin, request, queryset):
|
def delete_related_services(modeladmin, request, queryset):
|
||||||
opts = modeladmin.model._meta
|
opts = modeladmin.model._meta
|
||||||
app_label = opts.app_label
|
app_label = opts.app_label
|
||||||
|
|
||||||
using = router.db_for_write(modeladmin.model)
|
using = router.db_for_write(modeladmin.model)
|
||||||
collector = NestedObjects(using=using)
|
collector = NestedObjects(using=using)
|
||||||
collector.collect(queryset)
|
collector.collect(queryset)
|
||||||
registered_services = services.get()
|
registered_services = services.get()
|
||||||
related_services = []
|
related_services = []
|
||||||
to_delete = []
|
to_delete = []
|
||||||
|
|
||||||
admin_site = modeladmin.admin_site
|
admin_site = modeladmin.admin_site
|
||||||
|
|
||||||
def format(obj, account=False):
|
def format(obj, account=False):
|
||||||
has_admin = obj.__class__ in admin_site._registry
|
has_admin = obj.__class__ in admin_site._registry
|
||||||
opts = obj._meta
|
opts = obj._meta
|
||||||
no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_text(obj))
|
no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), force_text(obj))
|
||||||
|
|
||||||
if has_admin:
|
if has_admin:
|
||||||
try:
|
try:
|
||||||
admin_url = reverse(
|
admin_url = reverse(
|
||||||
|
@ -95,7 +95,7 @@ def delete_related_services(modeladmin, request, queryset):
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
# Change url doesn't exist -- don't display link to edit
|
# Change url doesn't exist -- don't display link to edit
|
||||||
return no_edit_link
|
return no_edit_link
|
||||||
|
|
||||||
# Display a link to the admin page.
|
# Display a link to the admin page.
|
||||||
context = (capfirst(opts.verbose_name), admin_url, obj)
|
context = (capfirst(opts.verbose_name), admin_url, obj)
|
||||||
if account:
|
if account:
|
||||||
|
@ -106,7 +106,7 @@ def delete_related_services(modeladmin, request, queryset):
|
||||||
# Don't display link to edit, because it either has no
|
# Don't display link to edit, because it either has no
|
||||||
# admin or is edited inline.
|
# admin or is edited inline.
|
||||||
return no_edit_link
|
return no_edit_link
|
||||||
|
|
||||||
def format_nested(objs, result):
|
def format_nested(objs, result):
|
||||||
if isinstance(objs, list):
|
if isinstance(objs, list):
|
||||||
current = []
|
current = []
|
||||||
|
@ -115,7 +115,7 @@ def delete_related_services(modeladmin, request, queryset):
|
||||||
result.append(current)
|
result.append(current)
|
||||||
else:
|
else:
|
||||||
result.append(format(objs))
|
result.append(format(objs))
|
||||||
|
|
||||||
for nested in collector.nested():
|
for nested in collector.nested():
|
||||||
if isinstance(nested, list):
|
if isinstance(nested, list):
|
||||||
# Is lists of objects
|
# Is lists of objects
|
||||||
|
@ -141,7 +141,7 @@ def delete_related_services(modeladmin, request, queryset):
|
||||||
# Prevent the deletion of the main system user, which will delete the account
|
# Prevent the deletion of the main system user, which will delete the account
|
||||||
main_systemuser = nested.main_systemuser
|
main_systemuser = nested.main_systemuser
|
||||||
related_services.append(format(nested, account=True))
|
related_services.append(format(nested, account=True))
|
||||||
|
|
||||||
# The user has already confirmed the deletion.
|
# The user has already confirmed the deletion.
|
||||||
# Do the deletion and return a None to display the change list view again.
|
# Do the deletion and return a None to display the change list view again.
|
||||||
if request.POST.get('post'):
|
if request.POST.get('post'):
|
||||||
|
@ -165,12 +165,12 @@ def delete_related_services(modeladmin, request, queryset):
|
||||||
modeladmin.message_user(request, msg, messages.SUCCESS)
|
modeladmin.message_user(request, msg, messages.SUCCESS)
|
||||||
# Return None to display the change list page again.
|
# Return None to display the change list page again.
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if len(queryset) == 1:
|
if len(queryset) == 1:
|
||||||
objects_name = force_text(opts.verbose_name)
|
objects_name = force_text(opts.verbose_name)
|
||||||
else:
|
else:
|
||||||
objects_name = force_text(opts.verbose_name_plural)
|
objects_name = force_text(opts.verbose_name_plural)
|
||||||
|
|
||||||
model_count = {}
|
model_count = {}
|
||||||
for model, objs in collector.model_objs.items():
|
for model, objs in collector.model_objs.items():
|
||||||
count = 0
|
count = 0
|
||||||
|
@ -220,10 +220,10 @@ def disable_selected(modeladmin, request, queryset, disable=True):
|
||||||
n)
|
n)
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
user = request.user
|
user = request.user
|
||||||
admin_site = modeladmin.admin_site
|
admin_site = modeladmin.admin_site
|
||||||
|
|
||||||
def format(obj):
|
def format(obj):
|
||||||
has_admin = obj.__class__ in admin_site._registry
|
has_admin = obj.__class__ in admin_site._registry
|
||||||
opts = obj._meta
|
opts = obj._meta
|
||||||
|
@ -238,7 +238,7 @@ def disable_selected(modeladmin, request, queryset, disable=True):
|
||||||
except NoReverseMatch:
|
except NoReverseMatch:
|
||||||
# Change url doesn't exist -- don't display link to edit
|
# Change url doesn't exist -- don't display link to edit
|
||||||
return no_edit_link
|
return no_edit_link
|
||||||
|
|
||||||
p = '%s.%s' % (opts.app_label, get_permission_codename('delete', opts))
|
p = '%s.%s' % (opts.app_label, get_permission_codename('delete', opts))
|
||||||
if not user.has_perm(p):
|
if not user.has_perm(p):
|
||||||
perms_needed.add(opts.verbose_name)
|
perms_needed.add(opts.verbose_name)
|
||||||
|
@ -249,19 +249,19 @@ def disable_selected(modeladmin, request, queryset, disable=True):
|
||||||
# Don't display link to edit, because it either has no
|
# Don't display link to edit, because it either has no
|
||||||
# admin or is edited inline.
|
# admin or is edited inline.
|
||||||
return no_edit_link
|
return no_edit_link
|
||||||
|
|
||||||
display = []
|
display = []
|
||||||
for account in queryset:
|
for account in queryset:
|
||||||
current = []
|
current = []
|
||||||
for related in account.get_services_to_disable():
|
for related in account.get_services_to_disable():
|
||||||
current.append(format(related))
|
current.append(format(related))
|
||||||
display.append([format(account), current])
|
display.append([format(account), current])
|
||||||
|
|
||||||
if len(queryset) == 1:
|
if len(queryset) == 1:
|
||||||
objects_name = force_text(opts.verbose_name)
|
objects_name = force_text(opts.verbose_name)
|
||||||
else:
|
else:
|
||||||
objects_name = force_text(opts.verbose_name_plural)
|
objects_name = force_text(opts.verbose_name_plural)
|
||||||
|
|
||||||
context = dict(
|
context = dict(
|
||||||
admin_site.each_context(request),
|
admin_site.each_context(request),
|
||||||
action_name='disable_selected' if disable else 'enable_selected',
|
action_name='disable_selected' if disable else 'enable_selected',
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.conf.urls import url
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from django.contrib.admin.utils import unquote
|
from django.contrib.admin.utils import unquote
|
||||||
from django.contrib.auth import admin as auth
|
from django.contrib.auth import admin as auth
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
@ -71,15 +71,15 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
||||||
)
|
)
|
||||||
change_view_actions = (disable_selected, service_report, enable_selected)
|
change_view_actions = (disable_selected, service_report, enable_selected)
|
||||||
ordering = ()
|
ordering = ()
|
||||||
|
|
||||||
main_systemuser_link = admin_link('main_systemuser')
|
main_systemuser_link = admin_link('main_systemuser')
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
if db_field.name == 'comments':
|
if db_field.name == 'comments':
|
||||||
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4})
|
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4})
|
||||||
return super(AccountAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(AccountAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
|
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
|
||||||
if not add:
|
if not add:
|
||||||
if request.method == 'GET' and not obj.is_active:
|
if request.method == 'GET' and not obj.is_active:
|
||||||
|
@ -96,7 +96,7 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
||||||
})
|
})
|
||||||
return super(AccountAdmin, self).render_change_form(
|
return super(AccountAdmin, self).render_change_form(
|
||||||
request, context, add, change, form_url, obj)
|
request, context, add, change, form_url, obj)
|
||||||
|
|
||||||
def get_fieldsets(self, request, obj=None):
|
def get_fieldsets(self, request, obj=None):
|
||||||
fieldsets = super(AccountAdmin, self).get_fieldsets(request, obj)
|
fieldsets = super(AccountAdmin, self).get_fieldsets(request, obj)
|
||||||
if not obj:
|
if not obj:
|
||||||
|
@ -106,7 +106,7 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
||||||
fieldsets = list(fieldsets)
|
fieldsets = list(fieldsets)
|
||||||
fieldsets.insert(1, (_("Related services"), {'fields': fields}))
|
fieldsets.insert(1, (_("Related services"), {'fields': fields}))
|
||||||
return fieldsets
|
return fieldsets
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
if not change:
|
if not change:
|
||||||
form.save_model(obj)
|
form.save_model(obj)
|
||||||
|
@ -133,7 +133,7 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
||||||
if msg:
|
if msg:
|
||||||
messages.warning(request, mark_safe(msg % context))
|
messages.warning(request, mark_safe(msg % context))
|
||||||
super(AccountAdmin, self).save_model(request, obj, form, change)
|
super(AccountAdmin, self).save_model(request, obj, form, change)
|
||||||
|
|
||||||
def get_change_view_actions(self, obj=None):
|
def get_change_view_actions(self, obj=None):
|
||||||
views = super().get_change_view_actions(obj=obj)
|
views = super().get_change_view_actions(obj=obj)
|
||||||
if obj is not None:
|
if obj is not None:
|
||||||
|
@ -141,7 +141,7 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
||||||
return [view for view in views if view.url_name != 'enable']
|
return [view for view in views if view.url_name != 'enable']
|
||||||
return [view for view in views if view.url_name != 'disable']
|
return [view for view in views if view.url_name != 'disable']
|
||||||
return views
|
return views
|
||||||
|
|
||||||
def get_actions(self, request):
|
def get_actions(self, request):
|
||||||
actions = super().get_actions(request)
|
actions = super().get_actions(request)
|
||||||
if 'delete_selected' in actions:
|
if 'delete_selected' in actions:
|
||||||
|
@ -157,7 +157,7 @@ class AccountListAdmin(AccountAdmin):
|
||||||
list_display = ('select_account', 'username', 'type', 'username')
|
list_display = ('select_account', 'username', 'type', 'username')
|
||||||
actions = None
|
actions = None
|
||||||
change_list_template = 'admin/accounts/account/select_account_list.html'
|
change_list_template = 'admin/accounts/account/select_account_list.html'
|
||||||
|
|
||||||
def select_account(self, instance):
|
def select_account(self, instance):
|
||||||
# TODO get query string from request.META['QUERY_STRING'] to preserve filters
|
# TODO get query string from request.META['QUERY_STRING'] to preserve filters
|
||||||
context = {
|
context = {
|
||||||
|
@ -169,7 +169,7 @@ class AccountListAdmin(AccountAdmin):
|
||||||
select_account.short_description = _("account")
|
select_account.short_description = _("account")
|
||||||
select_account.allow_tags = True
|
select_account.allow_tags = True
|
||||||
select_account.admin_order_field = 'username'
|
select_account.admin_order_field = 'username'
|
||||||
|
|
||||||
def changelist_view(self, request, extra_context=None):
|
def changelist_view(self, request, extra_context=None):
|
||||||
app_label = request.META['PATH_INFO'].split('/')[-5]
|
app_label = request.META['PATH_INFO'].split('/')[-5]
|
||||||
model = request.META['PATH_INFO'].split('/')[-4]
|
model = request.META['PATH_INFO'].split('/')[-4]
|
||||||
|
@ -206,7 +206,7 @@ class AccountAdminMixin(object):
|
||||||
change_form_template = 'admin/accounts/account/change_form.html'
|
change_form_template = 'admin/accounts/account/change_form.html'
|
||||||
account = None
|
account = None
|
||||||
list_select_related = ('account',)
|
list_select_related = ('account',)
|
||||||
|
|
||||||
def display_active(self, instance):
|
def display_active(self, instance):
|
||||||
if not instance.is_active:
|
if not instance.is_active:
|
||||||
return '<img src="%s" alt="False">' % static('admin/img/icon-no.svg')
|
return '<img src="%s" alt="False">' % static('admin/img/icon-no.svg')
|
||||||
|
@ -217,14 +217,14 @@ class AccountAdminMixin(object):
|
||||||
display_active.short_description = _("active")
|
display_active.short_description = _("active")
|
||||||
display_active.allow_tags = True
|
display_active.allow_tags = True
|
||||||
display_active.admin_order_field = 'is_active'
|
display_active.admin_order_field = 'is_active'
|
||||||
|
|
||||||
def account_link(self, instance):
|
def account_link(self, instance):
|
||||||
account = instance.account if instance.pk else self.account
|
account = instance.account if instance.pk else self.account
|
||||||
return admin_link()(account)
|
return admin_link()(account)
|
||||||
account_link.short_description = _("account")
|
account_link.short_description = _("account")
|
||||||
account_link.allow_tags = True
|
account_link.allow_tags = True
|
||||||
account_link.admin_order_field = 'account__username'
|
account_link.admin_order_field = 'account__username'
|
||||||
|
|
||||||
def get_form(self, request, obj=None, **kwargs):
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
""" Warns user when object's account is disabled """
|
""" Warns user when object's account is disabled """
|
||||||
form = super(AccountAdminMixin, self).get_form(request, obj, **kwargs)
|
form = super(AccountAdminMixin, self).get_form(request, obj, **kwargs)
|
||||||
|
@ -247,7 +247,7 @@ class AccountAdminMixin(object):
|
||||||
# Not available in POST
|
# Not available in POST
|
||||||
form.initial_account = self.get_changeform_initial_data(request).get('account')
|
form.initial_account = self.get_changeform_initial_data(request).get('account')
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def get_fields(self, request, obj=None):
|
def get_fields(self, request, obj=None):
|
||||||
""" remove account or account_link depending on the case """
|
""" remove account or account_link depending on the case """
|
||||||
fields = super(AccountAdminMixin, self).get_fields(request, obj)
|
fields = super(AccountAdminMixin, self).get_fields(request, obj)
|
||||||
|
@ -263,13 +263,13 @@ class AccountAdminMixin(object):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
""" provide account for filter_by_account_fields """
|
""" provide account for filter_by_account_fields """
|
||||||
if obj:
|
if obj:
|
||||||
self.account = obj.account
|
self.account = obj.account
|
||||||
return super(AccountAdminMixin, self).get_readonly_fields(request, obj)
|
return super(AccountAdminMixin, self).get_readonly_fields(request, obj)
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Filter by account """
|
""" Filter by account """
|
||||||
formfield = super(AccountAdminMixin, self).formfield_for_dbfield(db_field, **kwargs)
|
formfield = super(AccountAdminMixin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
@ -277,14 +277,14 @@ class AccountAdminMixin(object):
|
||||||
if self.account:
|
if self.account:
|
||||||
# Hack widget render in order to append ?account=id to the add url
|
# Hack widget render in order to append ?account=id to the add url
|
||||||
old_render = formfield.widget.render
|
old_render = formfield.widget.render
|
||||||
|
|
||||||
def render(*args, **kwargs):
|
def render(*args, **kwargs):
|
||||||
output = old_render(*args, **kwargs)
|
output = old_render(*args, **kwargs)
|
||||||
output = output.replace('/add/"', '/add/?account=%s"' % self.account.pk)
|
output = output.replace('/add/"', '/add/?account=%s"' % self.account.pk)
|
||||||
with_qargs = r'/add/?\1&account=%s"' % self.account.pk
|
with_qargs = r'/add/?\1&account=%s"' % self.account.pk
|
||||||
output = re.sub(r'/add/\?([^".]*)"', with_qargs, output)
|
output = re.sub(r'/add/\?([^".]*)"', with_qargs, output)
|
||||||
return mark_safe(output)
|
return mark_safe(output)
|
||||||
|
|
||||||
formfield.widget.render = render
|
formfield.widget.render = render
|
||||||
# Filter related object by account
|
# Filter related object by account
|
||||||
formfield.queryset = formfield.queryset.filter(account=self.account)
|
formfield.queryset = formfield.queryset.filter(account=self.account)
|
||||||
|
@ -302,21 +302,21 @@ class AccountAdminMixin(object):
|
||||||
formfield.initial = 1
|
formfield.initial = 1
|
||||||
formfield.queryset = formfield.queryset.order_by('username')
|
formfield.queryset = formfield.queryset.order_by('username')
|
||||||
return formfield
|
return formfield
|
||||||
|
|
||||||
def get_formset(self, request, obj=None, **kwargs):
|
def get_formset(self, request, obj=None, **kwargs):
|
||||||
""" provides form.account for convinience """
|
""" provides form.account for convinience """
|
||||||
formset = super(AccountAdminMixin, self).get_formset(request, obj, **kwargs)
|
formset = super(AccountAdminMixin, self).get_formset(request, obj, **kwargs)
|
||||||
formset.form.account = self.account
|
formset.form.account = self.account
|
||||||
formset.account = self.account
|
formset.account = self.account
|
||||||
return formset
|
return formset
|
||||||
|
|
||||||
def get_account_from_preserve_filters(self, request):
|
def get_account_from_preserve_filters(self, request):
|
||||||
preserved_filters = self.get_preserved_filters(request)
|
preserved_filters = self.get_preserved_filters(request)
|
||||||
preserved_filters = dict(parse_qsl(preserved_filters))
|
preserved_filters = dict(parse_qsl(preserved_filters))
|
||||||
cl_filters = preserved_filters.get('_changelist_filters')
|
cl_filters = preserved_filters.get('_changelist_filters')
|
||||||
if cl_filters:
|
if cl_filters:
|
||||||
return dict(parse_qsl(cl_filters)).get('account')
|
return dict(parse_qsl(cl_filters)).get('account')
|
||||||
|
|
||||||
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
|
def changeform_view(self, request, object_id=None, form_url='', extra_context=None):
|
||||||
account_id = self.get_account_from_preserve_filters(request)
|
account_id = self.get_account_from_preserve_filters(request)
|
||||||
if not object_id:
|
if not object_id:
|
||||||
|
@ -331,7 +331,7 @@ class AccountAdminMixin(object):
|
||||||
context.update(extra_context or {})
|
context.update(extra_context or {})
|
||||||
return super(AccountAdminMixin, self).changeform_view(
|
return super(AccountAdminMixin, self).changeform_view(
|
||||||
request, object_id, form_url=form_url, extra_context=context)
|
request, object_id, form_url=form_url, extra_context=context)
|
||||||
|
|
||||||
def changelist_view(self, request, extra_context=None):
|
def changelist_view(self, request, extra_context=None):
|
||||||
account_id = request.GET.get('account')
|
account_id = request.GET.get('account')
|
||||||
context = {}
|
context = {}
|
||||||
|
@ -367,7 +367,7 @@ class SelectAccountAdminMixin(AccountAdminMixin):
|
||||||
account = Account.objects.get(pk=request.GET['account'])
|
account = Account.objects.get(pk=request.GET['account'])
|
||||||
[setattr(inline, 'account', account) for inline in inlines]
|
[setattr(inline, 'account', account) for inline in inlines]
|
||||||
return inlines
|
return inlines
|
||||||
|
|
||||||
def get_urls(self):
|
def get_urls(self):
|
||||||
""" Hooks select account url """
|
""" Hooks select account url """
|
||||||
urls = super(AccountAdminMixin, self).get_urls()
|
urls = super(AccountAdminMixin, self).get_urls()
|
||||||
|
@ -381,7 +381,7 @@ class SelectAccountAdminMixin(AccountAdminMixin):
|
||||||
name='%s_%s_select_account' % info),
|
name='%s_%s_select_account' % info),
|
||||||
]
|
]
|
||||||
return select_urls + urls
|
return select_urls + urls
|
||||||
|
|
||||||
def add_view(self, request, form_url='', extra_context=None):
|
def add_view(self, request, form_url='', extra_context=None):
|
||||||
""" Redirects to select account view if required """
|
""" Redirects to select account view if required """
|
||||||
if request.user.is_superuser:
|
if request.user.is_superuser:
|
||||||
|
@ -406,7 +406,7 @@ class SelectAccountAdminMixin(AccountAdminMixin):
|
||||||
return super(AccountAdminMixin, self).add_view(
|
return super(AccountAdminMixin, self).add_view(
|
||||||
request, form_url=form_url, extra_context=context)
|
request, form_url=form_url, extra_context=context)
|
||||||
return HttpResponseRedirect('./select-account/?%s' % request.META['QUERY_STRING'])
|
return HttpResponseRedirect('./select-account/?%s' % request.META['QUERY_STRING'])
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
"""
|
"""
|
||||||
Given a model instance save it to the database.
|
Given a model instance save it to the database.
|
||||||
|
|
|
@ -5,7 +5,7 @@ from datetime import date
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.admin import helpers
|
from django.contrib.admin import helpers
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.forms.models import modelformset_factory
|
from django.forms.models import modelformset_factory
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
|
@ -179,7 +179,7 @@ def undo_billing(modeladmin, request, queryset):
|
||||||
group[line.order].append(line)
|
group[line.order].append(line)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
group[line.order] = [line]
|
group[line.order] = [line]
|
||||||
|
|
||||||
# Validate
|
# Validate
|
||||||
for order, lines in group.items():
|
for order, lines in group.items():
|
||||||
prev = None
|
prev = None
|
||||||
|
@ -211,7 +211,7 @@ def undo_billing(modeladmin, request, queryset):
|
||||||
messages.error(request, "Order does not have lines!.")
|
messages.error(request, "Order does not have lines!.")
|
||||||
order.billed_until = billed_until
|
order.billed_until = billed_until
|
||||||
order.billed_on = billed_on
|
order.billed_on = billed_on
|
||||||
|
|
||||||
# Commit changes
|
# Commit changes
|
||||||
norders, nlines = 0, 0
|
norders, nlines = 0, 0
|
||||||
for order, lines in group.items():
|
for order, lines in group.items():
|
||||||
|
@ -221,7 +221,7 @@ def undo_billing(modeladmin, request, queryset):
|
||||||
# TODO update order history undo billing
|
# TODO update order history undo billing
|
||||||
order.save(update_fields=('billed_until', 'billed_on'))
|
order.save(update_fields=('billed_until', 'billed_on'))
|
||||||
norders += 1
|
norders += 1
|
||||||
|
|
||||||
messages.success(request, _("%(norders)s orders and %(nlines)s lines undoed.") % {
|
messages.success(request, _("%(norders)s orders and %(nlines)s lines undoed.") % {
|
||||||
'nlines': nlines,
|
'nlines': nlines,
|
||||||
'norders': norders
|
'norders': norders
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django import forms
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from django.contrib.admin.utils import unquote
|
from django.contrib.admin.utils import unquote
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F, Sum, Prefetch
|
from django.db.models import F, Sum, Prefetch
|
||||||
from django.db.models.functions import Coalesce
|
from django.db.models.functions import Coalesce
|
||||||
|
@ -39,18 +39,18 @@ PAYMENT_STATE_COLORS = {
|
||||||
class BillSublineInline(admin.TabularInline):
|
class BillSublineInline(admin.TabularInline):
|
||||||
model = BillSubline
|
model = BillSubline
|
||||||
fields = ('description', 'total', 'type')
|
fields = ('description', 'total', 'type')
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
fields = super().get_readonly_fields(request, obj)
|
fields = super().get_readonly_fields(request, obj)
|
||||||
if obj and not obj.bill.is_open:
|
if obj and not obj.bill.is_open:
|
||||||
return self.get_fields(request)
|
return self.get_fields(request)
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
def get_max_num(self, request, obj=None):
|
def get_max_num(self, request, obj=None):
|
||||||
if obj and not obj.bill.is_open:
|
if obj and not obj.bill.is_open:
|
||||||
return 0
|
return 0
|
||||||
return super().get_max_num(request, obj)
|
return super().get_max_num(request, obj)
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
if obj and not obj.bill.is_open:
|
if obj and not obj.bill.is_open:
|
||||||
return False
|
return False
|
||||||
|
@ -64,9 +64,9 @@ class BillLineInline(admin.TabularInline):
|
||||||
'subtotal', 'display_total',
|
'subtotal', 'display_total',
|
||||||
)
|
)
|
||||||
readonly_fields = ('display_total', 'order_link')
|
readonly_fields = ('display_total', 'order_link')
|
||||||
|
|
||||||
order_link = admin_link('order', display='pk')
|
order_link = admin_link('order', display='pk')
|
||||||
|
|
||||||
def display_total(self, line):
|
def display_total(self, line):
|
||||||
if line.pk:
|
if line.pk:
|
||||||
total = line.compute_total()
|
total = line.compute_total()
|
||||||
|
@ -79,7 +79,7 @@ class BillLineInline(admin.TabularInline):
|
||||||
return '<a href="%s">%s</a>' % (url, total)
|
return '<a href="%s">%s</a>' % (url, total)
|
||||||
display_total.short_description = _("Total")
|
display_total.short_description = _("Total")
|
||||||
display_total.allow_tags = True
|
display_total.allow_tags = True
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
if db_field.name == 'description':
|
if db_field.name == 'description':
|
||||||
|
@ -87,7 +87,7 @@ class BillLineInline(admin.TabularInline):
|
||||||
elif db_field.name not in ('start_on', 'end_on'):
|
elif db_field.name not in ('start_on', 'end_on'):
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'6'})
|
kwargs['widget'] = forms.TextInput(attrs={'size':'6'})
|
||||||
return super().formfield_for_dbfield(db_field, **kwargs)
|
return super().formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super().get_queryset(request)
|
qs = super().get_queryset(request)
|
||||||
return qs.prefetch_related('sublines').select_related('order')
|
return qs.prefetch_related('sublines').select_related('order')
|
||||||
|
@ -96,14 +96,14 @@ class BillLineInline(admin.TabularInline):
|
||||||
class ClosedBillLineInline(BillLineInline):
|
class ClosedBillLineInline(BillLineInline):
|
||||||
# TODO reimplement as nested inlines when upstream
|
# TODO reimplement as nested inlines when upstream
|
||||||
# https://code.djangoproject.com/ticket/9025
|
# https://code.djangoproject.com/ticket/9025
|
||||||
|
|
||||||
fields = (
|
fields = (
|
||||||
'display_description', 'order_link', 'start_on', 'end_on', 'rate', 'quantity', 'tax',
|
'display_description', 'order_link', 'start_on', 'end_on', 'rate', 'quantity', 'tax',
|
||||||
'display_subtotal', 'display_total'
|
'display_subtotal', 'display_total'
|
||||||
)
|
)
|
||||||
readonly_fields = fields
|
readonly_fields = fields
|
||||||
can_delete = False
|
can_delete = False
|
||||||
|
|
||||||
def display_description(self, line):
|
def display_description(self, line):
|
||||||
descriptions = [line.description]
|
descriptions = [line.description]
|
||||||
for subline in line.sublines.all():
|
for subline in line.sublines.all():
|
||||||
|
@ -111,7 +111,7 @@ class ClosedBillLineInline(BillLineInline):
|
||||||
return '<br>'.join(descriptions)
|
return '<br>'.join(descriptions)
|
||||||
display_description.short_description = _("Description")
|
display_description.short_description = _("Description")
|
||||||
display_description.allow_tags = True
|
display_description.allow_tags = True
|
||||||
|
|
||||||
def display_subtotal(self, line):
|
def display_subtotal(self, line):
|
||||||
subtotals = [' ' + str(line.subtotal)]
|
subtotals = [' ' + str(line.subtotal)]
|
||||||
for subline in line.sublines.all():
|
for subline in line.sublines.all():
|
||||||
|
@ -119,13 +119,13 @@ class ClosedBillLineInline(BillLineInline):
|
||||||
return '<br>'.join(subtotals)
|
return '<br>'.join(subtotals)
|
||||||
display_subtotal.short_description = _("Subtotal")
|
display_subtotal.short_description = _("Subtotal")
|
||||||
display_subtotal.allow_tags = True
|
display_subtotal.allow_tags = True
|
||||||
|
|
||||||
def display_total(self, line):
|
def display_total(self, line):
|
||||||
if line.pk:
|
if line.pk:
|
||||||
return line.compute_total()
|
return line.compute_total()
|
||||||
display_total.short_description = _("Total")
|
display_total.short_description = _("Total")
|
||||||
display_total.allow_tags = True
|
display_total.allow_tags = True
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -158,28 +158,28 @@ class BillLineAdmin(admin.ModelAdmin):
|
||||||
list_select_related = ('bill', 'bill__account')
|
list_select_related = ('bill', 'bill__account')
|
||||||
search_fields = ('description', 'bill__number')
|
search_fields = ('description', 'bill__number')
|
||||||
inlines = (BillSublineInline,)
|
inlines = (BillSublineInline,)
|
||||||
|
|
||||||
account_link = admin_link('bill__account')
|
account_link = admin_link('bill__account')
|
||||||
bill_link = admin_link('bill')
|
bill_link = admin_link('bill')
|
||||||
order_link = admin_link('order')
|
order_link = admin_link('order')
|
||||||
amended_line_link = admin_link('amended_line')
|
amended_line_link = admin_link('amended_line')
|
||||||
|
|
||||||
def display_is_open(self, instance):
|
def display_is_open(self, instance):
|
||||||
return instance.bill.is_open
|
return instance.bill.is_open
|
||||||
display_is_open.short_description = _("Is open")
|
display_is_open.short_description = _("Is open")
|
||||||
display_is_open.boolean = True
|
display_is_open.boolean = True
|
||||||
|
|
||||||
def display_sublinetotal(self, instance):
|
def display_sublinetotal(self, instance):
|
||||||
total = instance.subline_total
|
total = instance.subline_total
|
||||||
return total if total is not None else '---'
|
return total if total is not None else '---'
|
||||||
display_sublinetotal.short_description = _("Sublines")
|
display_sublinetotal.short_description = _("Sublines")
|
||||||
display_sublinetotal.admin_order_field = 'subline_total'
|
display_sublinetotal.admin_order_field = 'subline_total'
|
||||||
|
|
||||||
def display_total(self, instance):
|
def display_total(self, instance):
|
||||||
return round(instance.computed_total or 0, 2)
|
return round(instance.computed_total or 0, 2)
|
||||||
display_total.short_description = _("Total")
|
display_total.short_description = _("Total")
|
||||||
display_total.admin_order_field = 'computed_total'
|
display_total.admin_order_field = 'computed_total'
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
fields = super().get_readonly_fields(request, obj)
|
fields = super().get_readonly_fields(request, obj)
|
||||||
if obj and not obj.bill.is_open:
|
if obj and not obj.bill.is_open:
|
||||||
|
@ -188,7 +188,7 @@ class BillLineAdmin(admin.ModelAdmin):
|
||||||
'subtotal', 'order_billed_on', 'order_billed_until'
|
'subtotal', 'order_billed_on', 'order_billed_until'
|
||||||
]
|
]
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super().get_queryset(request)
|
qs = super().get_queryset(request)
|
||||||
qs = qs.annotate(
|
qs = qs.annotate(
|
||||||
|
@ -196,7 +196,7 @@ class BillLineAdmin(admin.ModelAdmin):
|
||||||
computed_total=(F('subtotal') + Sum(Coalesce('sublines__total', 0))) * (1+F('tax')/100),
|
computed_total=(F('subtotal') + Sum(Coalesce('sublines__total', 0))) * (1+F('tax')/100),
|
||||||
)
|
)
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
if obj and not obj.bill.is_open:
|
if obj and not obj.bill.is_open:
|
||||||
return False
|
return False
|
||||||
|
@ -209,7 +209,7 @@ class BillLineManagerAdmin(BillLineAdmin):
|
||||||
if self.bill_ids:
|
if self.bill_ids:
|
||||||
return qset.filter(bill_id__in=self.bill_ids)
|
return qset.filter(bill_id__in=self.bill_ids)
|
||||||
return qset
|
return qset
|
||||||
|
|
||||||
def changelist_view(self, request, extra_context=None):
|
def changelist_view(self, request, extra_context=None):
|
||||||
GET_copy = request.GET.copy()
|
GET_copy = request.GET.copy()
|
||||||
bill_ids = GET_copy.pop('ids', None)
|
bill_ids = GET_copy.pop('ids', None)
|
||||||
|
@ -304,9 +304,9 @@ class AmendInline(BillAdminMixin, admin.TabularInline):
|
||||||
verbose_name_plural = _("Amends")
|
verbose_name_plural = _("Amends")
|
||||||
can_delete = False
|
can_delete = False
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
self_link = admin_link('__str__')
|
self_link = admin_link('__str__')
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -354,12 +354,12 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
|
||||||
'closed_on_display', 'updated_on_display', 'display_total_with_subtotals',
|
'closed_on_display', 'updated_on_display', 'display_total_with_subtotals',
|
||||||
)
|
)
|
||||||
date_hierarchy = 'closed_on'
|
date_hierarchy = 'closed_on'
|
||||||
|
|
||||||
created_on_display = admin_date('created_on', short_description=_("Created"))
|
created_on_display = admin_date('created_on', short_description=_("Created"))
|
||||||
closed_on_display = admin_date('closed_on', short_description=_("Closed"))
|
closed_on_display = admin_date('closed_on', short_description=_("Closed"))
|
||||||
updated_on_display = admin_date('updated_on', short_description=_("Updated"))
|
updated_on_display = admin_date('updated_on', short_description=_("Updated"))
|
||||||
amend_of_link = admin_link('amend_of')
|
amend_of_link = admin_link('amend_of')
|
||||||
|
|
||||||
# def amend_links(self, bill):
|
# def amend_links(self, bill):
|
||||||
# links = []
|
# links = []
|
||||||
# for amend in bill.amends.all():
|
# for amend in bill.amends.all():
|
||||||
|
@ -368,19 +368,19 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
|
||||||
# return '<br>'.join(links)
|
# return '<br>'.join(links)
|
||||||
# amend_links.short_description = _("Amends")
|
# amend_links.short_description = _("Amends")
|
||||||
# amend_links.allow_tags = True
|
# amend_links.allow_tags = True
|
||||||
|
|
||||||
def num_lines(self, bill):
|
def num_lines(self, bill):
|
||||||
return bill.lines__count
|
return bill.lines__count
|
||||||
num_lines.admin_order_field = 'lines__count'
|
num_lines.admin_order_field = 'lines__count'
|
||||||
num_lines.short_description = _("lines")
|
num_lines.short_description = _("lines")
|
||||||
|
|
||||||
def display_total(self, bill):
|
def display_total(self, bill):
|
||||||
currency = settings.BILLS_CURRENCY.lower()
|
currency = settings.BILLS_CURRENCY.lower()
|
||||||
return '%s &%s;' % (bill.compute_total(), currency)
|
return '%s &%s;' % (bill.compute_total(), currency)
|
||||||
display_total.allow_tags = True
|
display_total.allow_tags = True
|
||||||
display_total.short_description = _("total")
|
display_total.short_description = _("total")
|
||||||
display_total.admin_order_field = 'approx_total'
|
display_total.admin_order_field = 'approx_total'
|
||||||
|
|
||||||
def type_link(self, bill):
|
def type_link(self, bill):
|
||||||
bill_type = bill.type.lower()
|
bill_type = bill.type.lower()
|
||||||
url = reverse('admin:bills_%s_changelist' % bill_type)
|
url = reverse('admin:bills_%s_changelist' % bill_type)
|
||||||
|
@ -388,7 +388,7 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
|
||||||
type_link.allow_tags = True
|
type_link.allow_tags = True
|
||||||
type_link.short_description = _("type")
|
type_link.short_description = _("type")
|
||||||
type_link.admin_order_field = 'type'
|
type_link.admin_order_field = 'type'
|
||||||
|
|
||||||
def get_urls(self):
|
def get_urls(self):
|
||||||
""" Hook bill lines management URLs on bill admin """
|
""" Hook bill lines management URLs on bill admin """
|
||||||
urls = super().get_urls()
|
urls = super().get_urls()
|
||||||
|
@ -399,13 +399,13 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
|
||||||
name='bills_bill_manage_lines'),
|
name='bills_bill_manage_lines'),
|
||||||
]
|
]
|
||||||
return extra_urls + urls
|
return extra_urls + urls
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
fields = super().get_readonly_fields(request, obj)
|
fields = super().get_readonly_fields(request, obj)
|
||||||
if obj and not obj.is_open:
|
if obj and not obj.is_open:
|
||||||
fields += self.add_fields
|
fields += self.add_fields
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
def get_fieldsets(self, request, obj=None):
|
def get_fieldsets(self, request, obj=None):
|
||||||
fieldsets = super().get_fieldsets(request, obj)
|
fieldsets = super().get_fieldsets(request, obj)
|
||||||
if obj:
|
if obj:
|
||||||
|
@ -418,7 +418,7 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
|
||||||
if obj.is_open:
|
if obj.is_open:
|
||||||
fieldsets = fieldsets[0:-1]
|
fieldsets = fieldsets[0:-1]
|
||||||
return fieldsets
|
return fieldsets
|
||||||
|
|
||||||
def get_change_view_actions(self, obj=None):
|
def get_change_view_actions(self, obj=None):
|
||||||
actions = super().get_change_view_actions(obj)
|
actions = super().get_change_view_actions(obj)
|
||||||
exclude = []
|
exclude = []
|
||||||
|
@ -428,7 +428,7 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
|
||||||
if obj.type not in obj.AMEND_MAP:
|
if obj.type not in obj.AMEND_MAP:
|
||||||
exclude += ['amend_bills']
|
exclude += ['amend_bills']
|
||||||
return [action for action in actions if action.__name__ not in exclude]
|
return [action for action in actions if action.__name__ not in exclude]
|
||||||
|
|
||||||
def get_inline_instances(self, request, obj=None):
|
def get_inline_instances(self, request, obj=None):
|
||||||
cls = type(self)
|
cls = type(self)
|
||||||
if obj and not obj.is_open:
|
if obj and not obj.is_open:
|
||||||
|
@ -439,7 +439,7 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
|
||||||
else:
|
else:
|
||||||
cls.inlines = [BillLineInline]
|
cls.inlines = [BillLineInline]
|
||||||
return super().get_inline_instances(request, obj)
|
return super().get_inline_instances(request, obj)
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
if db_field.name == 'comments':
|
if db_field.name == 'comments':
|
||||||
|
@ -450,7 +450,7 @@ class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
|
||||||
if db_field.name == 'amend_of':
|
if db_field.name == 'amend_of':
|
||||||
formfield.queryset = formfield.queryset.filter(is_open=False)
|
formfield.queryset = formfield.queryset.filter(is_open=False)
|
||||||
return formfield
|
return formfield
|
||||||
|
|
||||||
def change_view(self, request, object_id, **kwargs):
|
def change_view(self, request, object_id, **kwargs):
|
||||||
# TODO raise404, here and everywhere
|
# TODO raise404, here and everywhere
|
||||||
bill = self.get_object(request, unquote(object_id))
|
bill = self.get_object(request, unquote(object_id))
|
||||||
|
@ -471,7 +471,7 @@ admin.site.register(BillLine, BillLineAdmin)
|
||||||
class BillContactInline(admin.StackedInline):
|
class BillContactInline(admin.StackedInline):
|
||||||
model = BillContact
|
model = BillContact
|
||||||
fields = ('name', 'address', ('city', 'zipcode'), 'country', 'vat')
|
fields = ('name', 'address', ('city', 'zipcode'), 'country', 'vat')
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
if db_field.name == 'name':
|
if db_field.name == 'name':
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib.admin import SimpleListFilter
|
from django.contrib.admin import SimpleListFilter
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -11,11 +11,11 @@ class BillTypeListFilter(SimpleListFilter):
|
||||||
""" Filter tickets by created_by according to request.user """
|
""" Filter tickets by created_by according to request.user """
|
||||||
title = 'Type'
|
title = 'Type'
|
||||||
parameter_name = ''
|
parameter_name = ''
|
||||||
|
|
||||||
def __init__(self, request, *args, **kwargs):
|
def __init__(self, request, *args, **kwargs):
|
||||||
super(BillTypeListFilter, self).__init__(request, *args, **kwargs)
|
super(BillTypeListFilter, self).__init__(request, *args, **kwargs)
|
||||||
self.request = request
|
self.request = request
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
return (
|
return (
|
||||||
('bill', _("All")),
|
('bill', _("All")),
|
||||||
|
@ -25,13 +25,13 @@ class BillTypeListFilter(SimpleListFilter):
|
||||||
('amendmentfee', _("Amendment fee")),
|
('amendmentfee', _("Amendment fee")),
|
||||||
('amendmentinvoice', _("Amendment invoice")),
|
('amendmentinvoice', _("Amendment invoice")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def value(self):
|
def value(self):
|
||||||
return self.request.path.split('/')[-2]
|
return self.request.path.split('/')[-2]
|
||||||
|
|
||||||
def choices(self, cl):
|
def choices(self, cl):
|
||||||
query = self.request.GET.urlencode()
|
query = self.request.GET.urlencode()
|
||||||
for lookup, title in self.lookup_choices:
|
for lookup, title in self.lookup_choices:
|
||||||
|
@ -45,7 +45,7 @@ class BillTypeListFilter(SimpleListFilter):
|
||||||
class TotalListFilter(SimpleListFilter):
|
class TotalListFilter(SimpleListFilter):
|
||||||
title = _("total")
|
title = _("total")
|
||||||
parameter_name = 'total'
|
parameter_name = 'total'
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
return (
|
return (
|
||||||
('gt', mark_safe("total > 0")),
|
('gt', mark_safe("total > 0")),
|
||||||
|
@ -53,7 +53,7 @@ class TotalListFilter(SimpleListFilter):
|
||||||
('eq', "total = 0"),
|
('eq', "total = 0"),
|
||||||
('ne', mark_safe("total ≠ 0")),
|
('ne', mark_safe("total ≠ 0")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
if self.value() == 'gt':
|
if self.value() == 'gt':
|
||||||
return queryset.filter(approx_total__gt=0)
|
return queryset.filter(approx_total__gt=0)
|
||||||
|
@ -70,13 +70,13 @@ class HasBillContactListFilter(SimpleListFilter):
|
||||||
""" Filter Nodes by group according to request.user """
|
""" Filter Nodes by group according to request.user """
|
||||||
title = _("has bill contact")
|
title = _("has bill contact")
|
||||||
parameter_name = 'bill'
|
parameter_name = 'bill'
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
return (
|
return (
|
||||||
('True', _("Yes")),
|
('True', _("Yes")),
|
||||||
('False', _("No")),
|
('False', _("No")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
if self.value() == 'True':
|
if self.value() == 'True':
|
||||||
return queryset.filter(billcontact__isnull=False)
|
return queryset.filter(billcontact__isnull=False)
|
||||||
|
@ -87,7 +87,7 @@ class HasBillContactListFilter(SimpleListFilter):
|
||||||
class PaymentStateListFilter(SimpleListFilter):
|
class PaymentStateListFilter(SimpleListFilter):
|
||||||
title = _("payment state")
|
title = _("payment state")
|
||||||
parameter_name = 'payment_state'
|
parameter_name = 'payment_state'
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
return (
|
return (
|
||||||
('OPEN', _("Open")),
|
('OPEN', _("Open")),
|
||||||
|
@ -95,7 +95,7 @@ class PaymentStateListFilter(SimpleListFilter):
|
||||||
('PENDING', _("Pending")),
|
('PENDING', _("Pending")),
|
||||||
('BAD_DEBT', _("Bad debt")),
|
('BAD_DEBT', _("Bad debt")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
# FIXME use queryset.computed_total instead of approx_total, bills.admin.BillAdmin.get_queryset
|
# FIXME use queryset.computed_total instead of approx_total, bills.admin.BillAdmin.get_queryset
|
||||||
Transaction = queryset.model.transactions.field.remote_field.related_model
|
Transaction = queryset.model.transactions.field.remote_field.related_model
|
||||||
|
@ -137,7 +137,7 @@ class PaymentStateListFilter(SimpleListFilter):
|
||||||
class AmendedListFilter(SimpleListFilter):
|
class AmendedListFilter(SimpleListFilter):
|
||||||
title = _("amended")
|
title = _("amended")
|
||||||
parameter_name = 'amended'
|
parameter_name = 'amended'
|
||||||
|
|
||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
return (
|
return (
|
||||||
('3', _("Closed amends")),
|
('3', _("Closed amends")),
|
||||||
|
@ -145,7 +145,7 @@ class AmendedListFilter(SimpleListFilter):
|
||||||
('1', _("Any amends")),
|
('1', _("Any amends")),
|
||||||
('0', _("No amends")),
|
('0', _("No amends")),
|
||||||
)
|
)
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
if self.value() is None:
|
if self.value() is None:
|
||||||
return queryset
|
return queryset
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import datetime
|
import datetime
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.core.validators import ValidationError, RegexValidator
|
from django.core.validators import ValidationError, RegexValidator
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import F, Sum
|
from django.db.models import F, Sum
|
||||||
|
@ -36,13 +36,13 @@ class BillContact(models.Model):
|
||||||
choices=settings.BILLS_CONTACT_COUNTRIES,
|
choices=settings.BILLS_CONTACT_COUNTRIES,
|
||||||
default=settings.BILLS_CONTACT_DEFAULT_COUNTRY)
|
default=settings.BILLS_CONTACT_DEFAULT_COUNTRY)
|
||||||
vat = models.CharField(_("VAT number"), max_length=64)
|
vat = models.CharField(_("VAT number"), max_length=64)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self.name or self.account.get_full_name()
|
return self.name or self.account.get_full_name()
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
self.vat = self.vat.strip()
|
self.vat = self.vat.strip()
|
||||||
self.city = self.city.strip()
|
self.city = self.city.strip()
|
||||||
|
@ -99,7 +99,7 @@ class Bill(models.Model):
|
||||||
INVOICE: AMENDMENTINVOICE,
|
INVOICE: AMENDMENTINVOICE,
|
||||||
FEE: AMENDMENTFEE,
|
FEE: AMENDMENTFEE,
|
||||||
}
|
}
|
||||||
|
|
||||||
number = models.CharField(_("number"), max_length=16, unique=True, blank=True)
|
number = models.CharField(_("number"), max_length=16, unique=True, blank=True)
|
||||||
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
account = models.ForeignKey('accounts.Account', verbose_name=_("account"),
|
||||||
related_name='%(class)s')
|
related_name='%(class)s')
|
||||||
|
@ -115,37 +115,37 @@ class Bill(models.Model):
|
||||||
# total = models.DecimalField(max_digits=12, decimal_places=2, null=True)
|
# total = models.DecimalField(max_digits=12, decimal_places=2, null=True)
|
||||||
comments = models.TextField(_("comments"), blank=True)
|
comments = models.TextField(_("comments"), blank=True)
|
||||||
html = models.TextField(_("HTML"), blank=True)
|
html = models.TextField(_("HTML"), blank=True)
|
||||||
|
|
||||||
objects = BillManager()
|
objects = BillManager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
get_latest_by = 'id'
|
get_latest_by = 'id'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.number
|
return self.number
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_class_type(cls):
|
def get_class_type(cls):
|
||||||
if cls is models.DEFERRED:
|
if cls is models.DEFERRED:
|
||||||
cls = cls.__base__
|
cls = cls.__base__
|
||||||
return cls.__name__.upper()
|
return cls.__name__.upper()
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def total(self):
|
def total(self):
|
||||||
return self.compute_total()
|
return self.compute_total()
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def seller(self):
|
def seller(self):
|
||||||
return Account.objects.get_main().billcontact
|
return Account.objects.get_main().billcontact
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def buyer(self):
|
def buyer(self):
|
||||||
return self.account.billcontact
|
return self.account.billcontact
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_multiple_pages(self):
|
def has_multiple_pages(self):
|
||||||
return self.type != self.FEE
|
return self.type != self.FEE
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def payment_state(self):
|
def payment_state(self):
|
||||||
if self.is_open or self.get_type() == self.PROFORMA:
|
if self.is_open or self.get_type() == self.PROFORMA:
|
||||||
|
@ -192,7 +192,7 @@ class Bill(models.Model):
|
||||||
elif executed:
|
elif executed:
|
||||||
return self.EXECUTED
|
return self.EXECUTED
|
||||||
return self.BAD_DEBT
|
return self.BAD_DEBT
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if self.amend_of_id:
|
if self.amend_of_id:
|
||||||
errors = {}
|
errors = {}
|
||||||
|
@ -206,27 +206,27 @@ class Bill(models.Model):
|
||||||
errors['amend_of'] = _("Related invoice is an amendment.")
|
errors['amend_of'] = _("Related invoice is an amendment.")
|
||||||
if errors:
|
if errors:
|
||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
|
|
||||||
def get_payment_state_display(self):
|
def get_payment_state_display(self):
|
||||||
value = self.payment_state
|
value = self.payment_state
|
||||||
return force_text(dict(self.PAYMENT_STATES).get(value, value))
|
return force_text(dict(self.PAYMENT_STATES).get(value, value))
|
||||||
|
|
||||||
def get_current_transaction(self):
|
def get_current_transaction(self):
|
||||||
return self.transactions.exclude_rejected().first()
|
return self.transactions.exclude_rejected().first()
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
return self.type or self.get_class_type()
|
return self.type or self.get_class_type()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_amend(self):
|
def is_amend(self):
|
||||||
return self.type in self.AMEND_MAP.values()
|
return self.type in self.AMEND_MAP.values()
|
||||||
|
|
||||||
def get_amend_type(self):
|
def get_amend_type(self):
|
||||||
amend_type = self.AMEND_MAP.get(self.type)
|
amend_type = self.AMEND_MAP.get(self.type)
|
||||||
if amend_type is None:
|
if amend_type is None:
|
||||||
raise TypeError("%s has no associated amend type." % self.type)
|
raise TypeError("%s has no associated amend type." % self.type)
|
||||||
return amend_type
|
return amend_type
|
||||||
|
|
||||||
def get_number(self):
|
def get_number(self):
|
||||||
cls = type(self)
|
cls = type(self)
|
||||||
if cls is models.DEFERRED:
|
if cls is models.DEFERRED:
|
||||||
|
@ -250,16 +250,16 @@ class Bill(models.Model):
|
||||||
zeros = (number_length - len(str(number))) * '0'
|
zeros = (number_length - len(str(number))) * '0'
|
||||||
number = zeros + str(number)
|
number = zeros + str(number)
|
||||||
return '{prefix}{year}{number}'.format(prefix=prefix, year=year, number=number)
|
return '{prefix}{year}{number}'.format(prefix=prefix, year=year, number=number)
|
||||||
|
|
||||||
def get_due_date(self, payment=None):
|
def get_due_date(self, payment=None):
|
||||||
now = timezone.now()
|
now = timezone.now()
|
||||||
if payment:
|
if payment:
|
||||||
return now + payment.get_due_delta()
|
return now + payment.get_due_delta()
|
||||||
return now + relativedelta(months=1)
|
return now + relativedelta(months=1)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse('admin:bills_bill_view', args=(self.pk,))
|
return reverse('admin:bills_bill_view', args=(self.pk,))
|
||||||
|
|
||||||
def close(self, payment=False):
|
def close(self, payment=False):
|
||||||
if not self.is_open:
|
if not self.is_open:
|
||||||
raise TypeError("Bill not in Open state.")
|
raise TypeError("Bill not in Open state.")
|
||||||
|
@ -278,10 +278,10 @@ class Bill(models.Model):
|
||||||
self.html = self.render(payment=payment)
|
self.html = self.render(payment=payment)
|
||||||
self.save()
|
self.save()
|
||||||
return transaction
|
return transaction
|
||||||
|
|
||||||
def get_billing_contact_emails(self):
|
def get_billing_contact_emails(self):
|
||||||
return self.account.get_contacts_emails(usages=(Contact.BILLING,))
|
return self.account.get_contacts_emails(usages=(Contact.BILLING,))
|
||||||
|
|
||||||
def send(self):
|
def send(self):
|
||||||
pdf = self.as_pdf()
|
pdf = self.as_pdf()
|
||||||
self.account.send_email(
|
self.account.send_email(
|
||||||
|
@ -298,7 +298,7 @@ class Bill(models.Model):
|
||||||
)
|
)
|
||||||
self.is_sent = True
|
self.is_sent = True
|
||||||
self.save(update_fields=['is_sent'])
|
self.save(update_fields=['is_sent'])
|
||||||
|
|
||||||
def render(self, payment=False, language=None):
|
def render(self, payment=False, language=None):
|
||||||
with translation.override(language or self.account.language):
|
with translation.override(language or self.account.language):
|
||||||
if payment is False:
|
if payment is False:
|
||||||
|
@ -325,22 +325,22 @@ class Bill(models.Model):
|
||||||
html = bill_template.render(context)
|
html = bill_template.render(context)
|
||||||
html = html.replace('-pageskip-', '<pdf:nextpage />')
|
html = html.replace('-pageskip-', '<pdf:nextpage />')
|
||||||
return html
|
return html
|
||||||
|
|
||||||
def as_pdf(self):
|
def as_pdf(self):
|
||||||
html = self.html or self.render()
|
html = self.html or self.render()
|
||||||
return html_to_pdf(html, pagination=self.has_multiple_pages)
|
return html_to_pdf(html, pagination=self.has_multiple_pages)
|
||||||
|
|
||||||
def updated(self):
|
def updated(self):
|
||||||
self.updated_on = timezone.now()
|
self.updated_on = timezone.now()
|
||||||
self.save(update_fields=('updated_on',))
|
self.save(update_fields=('updated_on',))
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.type:
|
if not self.type:
|
||||||
self.type = self.get_type()
|
self.type = self.get_type()
|
||||||
if not self.number:
|
if not self.number:
|
||||||
self.number = self.get_number()
|
self.number = self.get_number()
|
||||||
super(Bill, self).save(*args, **kwargs)
|
super(Bill, self).save(*args, **kwargs)
|
||||||
|
|
||||||
@cached
|
@cached
|
||||||
def compute_subtotals(self):
|
def compute_subtotals(self):
|
||||||
subtotals = {}
|
subtotals = {}
|
||||||
|
@ -354,21 +354,21 @@ class Bill(models.Model):
|
||||||
for tax, subtotal in subtotals.items():
|
for tax, subtotal in subtotals.items():
|
||||||
result[tax] = [subtotal, round(tax/100*subtotal, 2)]
|
result[tax] = [subtotal, round(tax/100*subtotal, 2)]
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@cached
|
@cached
|
||||||
def compute_base(self):
|
def compute_base(self):
|
||||||
bases = self.lines.annotate(
|
bases = self.lines.annotate(
|
||||||
bases=F('subtotal') + Sum(Coalesce('sublines__total', 0))
|
bases=F('subtotal') + Sum(Coalesce('sublines__total', 0))
|
||||||
)
|
)
|
||||||
return round(bases.aggregate(Sum('bases'))['bases__sum'] or 0, 2)
|
return round(bases.aggregate(Sum('bases'))['bases__sum'] or 0, 2)
|
||||||
|
|
||||||
@cached
|
@cached
|
||||||
def compute_tax(self):
|
def compute_tax(self):
|
||||||
taxes = self.lines.annotate(
|
taxes = self.lines.annotate(
|
||||||
taxes=(F('subtotal') + Coalesce(Sum('sublines__total'), 0)) * (F('tax')/100)
|
taxes=(F('subtotal') + Coalesce(Sum('sublines__total'), 0)) * (F('tax')/100)
|
||||||
)
|
)
|
||||||
return round(taxes.aggregate(Sum('taxes'))['taxes__sum'] or 0, 2)
|
return round(taxes.aggregate(Sum('taxes'))['taxes__sum'] or 0, 2)
|
||||||
|
|
||||||
@cached
|
@cached
|
||||||
def compute_total(self):
|
def compute_total(self):
|
||||||
if 'lines' in getattr(self, '_prefetched_objects_cache', ()):
|
if 'lines' in getattr(self, '_prefetched_objects_cache', ()):
|
||||||
|
@ -435,23 +435,23 @@ class BillLine(models.Model):
|
||||||
# Amendment
|
# Amendment
|
||||||
amended_line = models.ForeignKey('self', verbose_name=_("amended line"),
|
amended_line = models.ForeignKey('self', verbose_name=_("amended line"),
|
||||||
related_name='amendment_lines', null=True, blank=True)
|
related_name='amendment_lines', null=True, blank=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
get_latest_by = 'id'
|
get_latest_by = 'id'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "#%i" % self.pk if self.pk else self.description
|
return "#%i" % self.pk if self.pk else self.description
|
||||||
|
|
||||||
def get_verbose_quantity(self):
|
def get_verbose_quantity(self):
|
||||||
return self.verbose_quantity or self.quantity
|
return self.verbose_quantity or self.quantity
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if not self.verbose_quantity:
|
if not self.verbose_quantity:
|
||||||
quantity = str(self.quantity)
|
quantity = str(self.quantity)
|
||||||
# Strip trailing zeros
|
# Strip trailing zeros
|
||||||
if quantity.endswith('0'):
|
if quantity.endswith('0'):
|
||||||
self.verbose_quantity = quantity.strip('0').strip('.')
|
self.verbose_quantity = quantity.strip('0').strip('.')
|
||||||
|
|
||||||
def get_verbose_period(self):
|
def get_verbose_period(self):
|
||||||
from django.template.defaultfilters import date
|
from django.template.defaultfilters import date
|
||||||
date_format = "N 'y"
|
date_format = "N 'y"
|
||||||
|
@ -467,7 +467,7 @@ class BillLine(models.Model):
|
||||||
if ini == end:
|
if ini == end:
|
||||||
return ini
|
return ini
|
||||||
return "{ini} / {end}".format(ini=ini, end=end)
|
return "{ini} / {end}".format(ini=ini, end=end)
|
||||||
|
|
||||||
@cached
|
@cached
|
||||||
def compute_total(self):
|
def compute_total(self):
|
||||||
total = self.subtotal or 0
|
total = self.subtotal or 0
|
||||||
|
@ -478,7 +478,7 @@ class BillLine(models.Model):
|
||||||
else:
|
else:
|
||||||
total += self.sublines.aggregate(sub_total=Sum('total'))['sub_total'] or 0
|
total += self.sublines.aggregate(sub_total=Sum('total'))['sub_total'] or 0
|
||||||
return round(total, 2)
|
return round(total, 2)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return change_url(self)
|
return change_url(self)
|
||||||
|
|
||||||
|
@ -493,12 +493,12 @@ class BillSubline(models.Model):
|
||||||
(COMPENSATION, _("Compensation")),
|
(COMPENSATION, _("Compensation")),
|
||||||
(OTHER, _("Other")),
|
(OTHER, _("Other")),
|
||||||
)
|
)
|
||||||
|
|
||||||
# TODO: order info for undoing
|
# TODO: order info for undoing
|
||||||
line = models.ForeignKey(BillLine, verbose_name=_("bill line"), related_name='sublines')
|
line = models.ForeignKey(BillLine, verbose_name=_("bill line"), related_name='sublines')
|
||||||
description = models.CharField(_("description"), max_length=256)
|
description = models.CharField(_("description"), max_length=256)
|
||||||
total = models.DecimalField(max_digits=12, decimal_places=2)
|
total = models.DecimalField(max_digits=12, decimal_places=2)
|
||||||
type = models.CharField(_("type"), max_length=16, choices=TYPES, default=OTHER)
|
type = models.CharField(_("type"), max_length=16, choices=TYPES, default=OTHER)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "%s %i" % (self.description, self.total)
|
return "%s %i" % (self.description, self.total)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import unittest
|
||||||
import MySQLdb
|
import MySQLdb
|
||||||
from django.conf import settings as djsettings
|
from django.conf import settings as djsettings
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from orchestra.admin.utils import change_url
|
from orchestra.admin.utils import change_url
|
||||||
from orchestra.contrib.orchestration.models import Route, Server
|
from orchestra.contrib.orchestration.models import Route, Server
|
||||||
from orchestra.utils.sys import sshrun
|
from orchestra.utils.sys import sshrun
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.functions import Concat, Coalesce
|
from django.db.models.functions import Concat, Coalesce
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
@ -32,18 +32,18 @@ class DomainInline(admin.TabularInline):
|
||||||
readonly_fields = ('domain_link', 'display_records', 'account_link')
|
readonly_fields = ('domain_link', 'display_records', 'account_link')
|
||||||
extra = 0
|
extra = 0
|
||||||
verbose_name_plural = _("Subdomains")
|
verbose_name_plural = _("Subdomains")
|
||||||
|
|
||||||
domain_link = admin_link('__str__')
|
domain_link = admin_link('__str__')
|
||||||
domain_link.short_description = _("Name")
|
domain_link.short_description = _("Name")
|
||||||
account_link = admin_link('account')
|
account_link = admin_link('account')
|
||||||
|
|
||||||
def display_records(self, domain):
|
def display_records(self, domain):
|
||||||
return ', '.join([record.type for record in domain.records.all()])
|
return ', '.join([record.type for record in domain.records.all()])
|
||||||
display_records.short_description = _("Declared records")
|
display_records.short_description = _("Declared records")
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
""" Order by structured name and imporve performance """
|
""" Order by structured name and imporve performance """
|
||||||
qs = super(DomainInline, self).get_queryset(request)
|
qs = super(DomainInline, self).get_queryset(request)
|
||||||
|
@ -66,9 +66,9 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
add_form = BatchDomainCreationAdminForm
|
add_form = BatchDomainCreationAdminForm
|
||||||
actions = (edit_records, set_soa, list_accounts)
|
actions = (edit_records, set_soa, list_accounts)
|
||||||
change_view_actions = (view_zone, edit_records)
|
change_view_actions = (view_zone, edit_records)
|
||||||
|
|
||||||
top_link = admin_link('top')
|
top_link = admin_link('top')
|
||||||
|
|
||||||
def structured_name(self, domain):
|
def structured_name(self, domain):
|
||||||
if domain.is_top:
|
if domain.is_top:
|
||||||
return domain.name
|
return domain.name
|
||||||
|
@ -76,13 +76,13 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
structured_name.short_description = _("name")
|
structured_name.short_description = _("name")
|
||||||
structured_name.allow_tags = True
|
structured_name.allow_tags = True
|
||||||
structured_name.admin_order_field = 'structured_name'
|
structured_name.admin_order_field = 'structured_name'
|
||||||
|
|
||||||
def display_is_top(self, domain):
|
def display_is_top(self, domain):
|
||||||
return domain.is_top
|
return domain.is_top
|
||||||
display_is_top.short_description = _("Is top")
|
display_is_top.short_description = _("Is top")
|
||||||
display_is_top.boolean = True
|
display_is_top.boolean = True
|
||||||
display_is_top.admin_order_field = 'top'
|
display_is_top.admin_order_field = 'top'
|
||||||
|
|
||||||
def display_websites(self, domain):
|
def display_websites(self, domain):
|
||||||
if apps.isinstalled('orchestra.contrib.websites'):
|
if apps.isinstalled('orchestra.contrib.websites'):
|
||||||
websites = domain.websites.all()
|
websites = domain.websites.all()
|
||||||
|
@ -107,7 +107,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
display_websites.admin_order_field = 'websites__name'
|
display_websites.admin_order_field = 'websites__name'
|
||||||
display_websites.short_description = _("Websites")
|
display_websites.short_description = _("Websites")
|
||||||
display_websites.allow_tags = True
|
display_websites.allow_tags = True
|
||||||
|
|
||||||
def display_addresses(self, domain):
|
def display_addresses(self, domain):
|
||||||
if apps.isinstalled('orchestra.contrib.mailboxes'):
|
if apps.isinstalled('orchestra.contrib.mailboxes'):
|
||||||
add_url = reverse('admin:mailboxes_address_add')
|
add_url = reverse('admin:mailboxes_address_add')
|
||||||
|
@ -127,7 +127,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
display_addresses.short_description = _("Addresses")
|
display_addresses.short_description = _("Addresses")
|
||||||
display_addresses.admin_order_field = 'addresses__count'
|
display_addresses.admin_order_field = 'addresses__count'
|
||||||
display_addresses.allow_tags = True
|
display_addresses.allow_tags = True
|
||||||
|
|
||||||
def implicit_records(self, domain):
|
def implicit_records(self, domain):
|
||||||
defaults = []
|
defaults = []
|
||||||
types = set(domain.records.values_list('type', flat=True))
|
types = set(domain.records.values_list('type', flat=True))
|
||||||
|
@ -149,7 +149,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
return '<br>'.join(lines)
|
return '<br>'.join(lines)
|
||||||
implicit_records.short_description = _("Implicit records")
|
implicit_records.short_description = _("Implicit records")
|
||||||
implicit_records.allow_tags = True
|
implicit_records.allow_tags = True
|
||||||
|
|
||||||
def get_fieldsets(self, request, obj=None):
|
def get_fieldsets(self, request, obj=None):
|
||||||
""" Add SOA fields when domain is top """
|
""" Add SOA fields when domain is top """
|
||||||
fieldsets = super(DomainAdmin, self).get_fieldsets(request, obj)
|
fieldsets = super(DomainAdmin, self).get_fieldsets(request, obj)
|
||||||
|
@ -175,13 +175,13 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
if 'top_link' not in existing:
|
if 'top_link' not in existing:
|
||||||
fieldsets[0][1]['fields'].insert(2, 'top_link')
|
fieldsets[0][1]['fields'].insert(2, 'top_link')
|
||||||
return fieldsets
|
return fieldsets
|
||||||
|
|
||||||
def get_inline_instances(self, request, obj=None):
|
def get_inline_instances(self, request, obj=None):
|
||||||
inlines = super(DomainAdmin, self).get_inline_instances(request, obj)
|
inlines = super(DomainAdmin, self).get_inline_instances(request, obj)
|
||||||
if not obj or not obj.is_top:
|
if not obj or not obj.is_top:
|
||||||
return [inline for inline in inlines if type(inline) != DomainInline]
|
return [inline for inline in inlines if type(inline) != DomainInline]
|
||||||
return inlines
|
return inlines
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
""" Order by structured name and imporve performance """
|
""" Order by structured name and imporve performance """
|
||||||
qs = super(DomainAdmin, self).get_queryset(request)
|
qs = super(DomainAdmin, self).get_queryset(request)
|
||||||
|
@ -196,7 +196,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
if apps.isinstalled('orchestra.contrib.mailboxes'):
|
if apps.isinstalled('orchestra.contrib.mailboxes'):
|
||||||
qs = qs.annotate(models.Count('addresses'))
|
qs = qs.annotate(models.Count('addresses'))
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
""" batch domain creation support """
|
""" batch domain creation support """
|
||||||
super(DomainAdmin, self).save_model(request, obj, form, change)
|
super(DomainAdmin, self).save_model(request, obj, form, change)
|
||||||
|
@ -205,7 +205,7 @@ class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
for name in form.extra_names:
|
for name in form.extra_names:
|
||||||
domain = Domain.objects.create(name=name, account_id=obj.account_id)
|
domain = Domain.objects.create(name=name, account_id=obj.account_id)
|
||||||
self.extra_domains.append(domain)
|
self.extra_domains.append(domain)
|
||||||
|
|
||||||
def save_related(self, request, form, formsets, change):
|
def save_related(self, request, form, formsets, change):
|
||||||
""" batch domain creation support """
|
""" batch domain creation support """
|
||||||
super(DomainAdmin, self).save_related(request, form, formsets, change)
|
super(DomainAdmin, self).save_related(request, form, formsets, change)
|
||||||
|
|
|
@ -4,7 +4,7 @@ import socket
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
|
||||||
from django.conf import settings as djsettings
|
from django.conf import settings as djsettings
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from selenium.webdriver.support.select import Select
|
from selenium.webdriver.support.select import Select
|
||||||
|
|
||||||
from orchestra.contrib.orchestration.models import Server, Route
|
from orchestra.contrib.orchestration.models import Server, Route
|
||||||
|
@ -23,7 +23,7 @@ class DomainTestMixin(object):
|
||||||
SLAVE_SERVER = os.environ.get('ORCHESTRA_SLAVE_SERVER', 'localhost')
|
SLAVE_SERVER = os.environ.get('ORCHESTRA_SLAVE_SERVER', 'localhost')
|
||||||
MASTER_SERVER_ADDR = socket.gethostbyname(MASTER_SERVER)
|
MASTER_SERVER_ADDR = socket.gethostbyname(MASTER_SERVER)
|
||||||
SLAVE_SERVER_ADDR = socket.gethostbyname(SLAVE_SERVER)
|
SLAVE_SERVER_ADDR = socket.gethostbyname(SLAVE_SERVER)
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
djsettings.DEBUG = True
|
djsettings.DEBUG = True
|
||||||
super(DomainTestMixin, self).setUp()
|
super(DomainTestMixin, self).setUp()
|
||||||
|
@ -53,19 +53,19 @@ class DomainTestMixin(object):
|
||||||
(Record.CNAME, 'external.server.org.'),
|
(Record.CNAME, 'external.server.org.'),
|
||||||
)
|
)
|
||||||
self.django_domain_name = 'django%s.lan' % random_ascii(10)
|
self.django_domain_name = 'django%s.lan' % random_ascii(10)
|
||||||
|
|
||||||
def add_route(self):
|
def add_route(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def add(self, domain_name, records):
|
def add(self, domain_name, records):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def delete(self, domain_name, records):
|
def delete(self, domain_name, records):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def update(self, domain_name, records):
|
def update(self, domain_name, records):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def validate_add(self, server_addr, domain_name):
|
def validate_add(self, server_addr, domain_name):
|
||||||
context = {
|
context = {
|
||||||
'domain_name': domain_name,
|
'domain_name': domain_name,
|
||||||
|
@ -81,7 +81,7 @@ class DomainTestMixin(object):
|
||||||
self.assertEqual('%s.' % settings.DOMAINS_DEFAULT_NAME_SERVER, soa[4])
|
self.assertEqual('%s.' % settings.DOMAINS_DEFAULT_NAME_SERVER, soa[4])
|
||||||
hostmaster = utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER)
|
hostmaster = utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER)
|
||||||
self.assertEqual(hostmaster, soa[5])
|
self.assertEqual(hostmaster, soa[5])
|
||||||
|
|
||||||
dig_ns = 'dig @%(server_addr)s %(domain_name)s NS|grep "\sNS\s"'
|
dig_ns = 'dig @%(server_addr)s %(domain_name)s NS|grep "\sNS\s"'
|
||||||
name_servers = run(dig_ns % context).stdout
|
name_servers = run(dig_ns % context).stdout
|
||||||
# testdomain.org. 3600 IN NS ns1.orchestra.lan.
|
# testdomain.org. 3600 IN NS ns1.orchestra.lan.
|
||||||
|
@ -95,7 +95,7 @@ class DomainTestMixin(object):
|
||||||
self.assertEqual('IN', ns[2])
|
self.assertEqual('IN', ns[2])
|
||||||
self.assertEqual('NS', ns[3])
|
self.assertEqual('NS', ns[3])
|
||||||
self.assertIn(ns[4], ns_records)
|
self.assertIn(ns[4], ns_records)
|
||||||
|
|
||||||
dig_mx = 'dig @%(server_addr)s %(domain_name)s MX|grep "\sMX\s"'
|
dig_mx = 'dig @%(server_addr)s %(domain_name)s MX|grep "\sMX\s"'
|
||||||
mail_servers = run(dig_mx % context).stdout
|
mail_servers = run(dig_mx % context).stdout
|
||||||
for mx in mail_servers.splitlines():
|
for mx in mail_servers.splitlines():
|
||||||
|
@ -107,7 +107,7 @@ class DomainTestMixin(object):
|
||||||
self.assertEqual('MX', mx[3])
|
self.assertEqual('MX', mx[3])
|
||||||
self.assertIn(mx[4], ['10', '20'])
|
self.assertIn(mx[4], ['10', '20'])
|
||||||
self.assertIn(mx[5], ['mail2.orchestra.lan.', 'mail.orchestra.lan.'])
|
self.assertIn(mx[5], ['mail2.orchestra.lan.', 'mail.orchestra.lan.'])
|
||||||
|
|
||||||
def validate_delete(self, server_addr, domain_name):
|
def validate_delete(self, server_addr, domain_name):
|
||||||
context = {
|
context = {
|
||||||
'domain_name': domain_name,
|
'domain_name': domain_name,
|
||||||
|
@ -122,7 +122,7 @@ class DomainTestMixin(object):
|
||||||
self.assertNotEqual('%s.' % settings.DOMAINS_DEFAULT_NAME_SERVER, soa[4])
|
self.assertNotEqual('%s.' % settings.DOMAINS_DEFAULT_NAME_SERVER, soa[4])
|
||||||
hostmaster = utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER)
|
hostmaster = utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER)
|
||||||
self.assertNotEqual(hostmaster, soa[5])
|
self.assertNotEqual(hostmaster, soa[5])
|
||||||
|
|
||||||
def validate_update(self, server_addr, domain_name):
|
def validate_update(self, server_addr, domain_name):
|
||||||
context = {
|
context = {
|
||||||
'domain_name': domain_name,
|
'domain_name': domain_name,
|
||||||
|
@ -138,7 +138,7 @@ class DomainTestMixin(object):
|
||||||
self.assertEqual('%s.' % settings.DOMAINS_DEFAULT_NAME_SERVER, soa[4])
|
self.assertEqual('%s.' % settings.DOMAINS_DEFAULT_NAME_SERVER, soa[4])
|
||||||
hostmaster = utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER)
|
hostmaster = utils.format_hostmaster(settings.DOMAINS_DEFAULT_HOSTMASTER)
|
||||||
self.assertEqual(hostmaster, soa[5])
|
self.assertEqual(hostmaster, soa[5])
|
||||||
|
|
||||||
dig_ns = 'dig @%(server_addr)s %(domain_name)s NS |grep "\sNS\s"'
|
dig_ns = 'dig @%(server_addr)s %(domain_name)s NS |grep "\sNS\s"'
|
||||||
name_servers = run(dig_ns % context).stdout
|
name_servers = run(dig_ns % context).stdout
|
||||||
ns_records = ['ns1.%s.' % self.domain_name, 'ns2.%s.' % self.domain_name]
|
ns_records = ['ns1.%s.' % self.domain_name, 'ns2.%s.' % self.domain_name]
|
||||||
|
@ -151,7 +151,7 @@ class DomainTestMixin(object):
|
||||||
self.assertEqual('IN', ns[2])
|
self.assertEqual('IN', ns[2])
|
||||||
self.assertEqual('NS', ns[3])
|
self.assertEqual('NS', ns[3])
|
||||||
self.assertIn(ns[4], ns_records)
|
self.assertIn(ns[4], ns_records)
|
||||||
|
|
||||||
dig_mx = 'dig @%(server_addr)s %(domain_name)s MX | grep "\sMX\s"'
|
dig_mx = 'dig @%(server_addr)s %(domain_name)s MX | grep "\sMX\s"'
|
||||||
mx = run(dig_mx % context).stdout.split()
|
mx = run(dig_mx % context).stdout.split()
|
||||||
# testdomain.org. 3600 IN MX 10 orchestra.lan.
|
# testdomain.org. 3600 IN MX 10 orchestra.lan.
|
||||||
|
@ -161,7 +161,7 @@ class DomainTestMixin(object):
|
||||||
self.assertEqual('MX', mx[3])
|
self.assertEqual('MX', mx[3])
|
||||||
self.assertIn(mx[4], ['30', '40'])
|
self.assertIn(mx[4], ['30', '40'])
|
||||||
self.assertIn(mx[5], ['mail3.orchestra.lan.', 'mail4.orchestra.lan.'])
|
self.assertIn(mx[5], ['mail3.orchestra.lan.', 'mail4.orchestra.lan.'])
|
||||||
|
|
||||||
def validate_www_update(self, server_addr, domain_name):
|
def validate_www_update(self, server_addr, domain_name):
|
||||||
context = {
|
context = {
|
||||||
'domain_name': domain_name,
|
'domain_name': domain_name,
|
||||||
|
@ -175,7 +175,7 @@ class DomainTestMixin(object):
|
||||||
self.assertEqual('IN', cname[2])
|
self.assertEqual('IN', cname[2])
|
||||||
self.assertEqual('CNAME', cname[3])
|
self.assertEqual('CNAME', cname[3])
|
||||||
self.assertEqual('external.server.org.', cname[4])
|
self.assertEqual('external.server.org.', cname[4])
|
||||||
|
|
||||||
def test_add(self):
|
def test_add(self):
|
||||||
self.add(self.ns1_name, self.ns1_records)
|
self.add(self.ns1_name, self.ns1_records)
|
||||||
self.add(self.ns2_name, self.ns2_records)
|
self.add(self.ns2_name, self.ns2_records)
|
||||||
|
@ -184,7 +184,7 @@ class DomainTestMixin(object):
|
||||||
self.validate_add(self.MASTER_SERVER_ADDR, self.domain_name)
|
self.validate_add(self.MASTER_SERVER_ADDR, self.domain_name)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
self.validate_add(self.SLAVE_SERVER_ADDR, self.domain_name)
|
self.validate_add(self.SLAVE_SERVER_ADDR, self.domain_name)
|
||||||
|
|
||||||
def test_delete(self):
|
def test_delete(self):
|
||||||
self.add(self.ns1_name, self.ns1_records)
|
self.add(self.ns1_name, self.ns1_records)
|
||||||
self.add(self.ns2_name, self.ns2_records)
|
self.add(self.ns2_name, self.ns2_records)
|
||||||
|
@ -193,7 +193,7 @@ class DomainTestMixin(object):
|
||||||
for name in [self.domain_name, self.ns1_name, self.ns2_name]:
|
for name in [self.domain_name, self.ns1_name, self.ns2_name]:
|
||||||
self.validate_delete(self.MASTER_SERVER_ADDR, name)
|
self.validate_delete(self.MASTER_SERVER_ADDR, name)
|
||||||
self.validate_delete(self.SLAVE_SERVER_ADDR, name)
|
self.validate_delete(self.SLAVE_SERVER_ADDR, name)
|
||||||
|
|
||||||
def test_update(self):
|
def test_update(self):
|
||||||
self.add(self.ns1_name, self.ns1_records)
|
self.add(self.ns1_name, self.ns1_records)
|
||||||
self.add(self.ns2_name, self.ns2_records)
|
self.add(self.ns2_name, self.ns2_records)
|
||||||
|
@ -209,7 +209,7 @@ class DomainTestMixin(object):
|
||||||
self.validate_www_update(self.MASTER_SERVER_ADDR, self.domain_name)
|
self.validate_www_update(self.MASTER_SERVER_ADDR, self.domain_name)
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
self.validate_www_update(self.SLAVE_SERVER_ADDR, self.domain_name)
|
self.validate_www_update(self.SLAVE_SERVER_ADDR, self.domain_name)
|
||||||
|
|
||||||
def test_add_add_delete_delete(self):
|
def test_add_add_delete_delete(self):
|
||||||
self.add(self.ns1_name, self.ns1_records)
|
self.add(self.ns1_name, self.ns1_records)
|
||||||
self.add(self.ns2_name, self.ns2_records)
|
self.add(self.ns2_name, self.ns2_records)
|
||||||
|
@ -221,7 +221,7 @@ class DomainTestMixin(object):
|
||||||
self.delete(self.django_domain_name)
|
self.delete(self.django_domain_name)
|
||||||
self.validate_delete(self.MASTER_SERVER_ADDR, self.django_domain_name)
|
self.validate_delete(self.MASTER_SERVER_ADDR, self.django_domain_name)
|
||||||
self.validate_delete(self.SLAVE_SERVER_ADDR, self.django_domain_name)
|
self.validate_delete(self.SLAVE_SERVER_ADDR, self.django_domain_name)
|
||||||
|
|
||||||
def test_bad_creation(self):
|
def test_bad_creation(self):
|
||||||
self.assertRaises((self.rest.ResponseStatusError, AssertionError),
|
self.assertRaises((self.rest.ResponseStatusError, AssertionError),
|
||||||
self.add, self.domain_name, self.domain_records)
|
self.add, self.domain_name, self.domain_records)
|
||||||
|
@ -232,7 +232,7 @@ class AdminDomainMixin(DomainTestMixin):
|
||||||
super(AdminDomainMixin, self).setUp()
|
super(AdminDomainMixin, self).setUp()
|
||||||
self.add_route()
|
self.add_route()
|
||||||
self.admin_login()
|
self.admin_login()
|
||||||
|
|
||||||
def _add_records(self, records):
|
def _add_records(self, records):
|
||||||
self.selenium.find_element_by_link_text('Add another Record').click()
|
self.selenium.find_element_by_link_text('Add another Record').click()
|
||||||
for i, record in zip(range(0, len(records)), records):
|
for i, record in zip(range(0, len(records)), records):
|
||||||
|
@ -244,29 +244,29 @@ class AdminDomainMixin(DomainTestMixin):
|
||||||
value_input.clear()
|
value_input.clear()
|
||||||
value_input.send_keys(value)
|
value_input.send_keys(value)
|
||||||
return value_input
|
return value_input
|
||||||
|
|
||||||
@snapshot_on_error
|
@snapshot_on_error
|
||||||
def add(self, domain_name, records):
|
def add(self, domain_name, records):
|
||||||
add = reverse('admin:domains_domain_add')
|
add = reverse('admin:domains_domain_add')
|
||||||
url = self.live_server_url + add
|
url = self.live_server_url + add
|
||||||
self.selenium.get(url)
|
self.selenium.get(url)
|
||||||
|
|
||||||
name = self.selenium.find_element_by_id('id_name')
|
name = self.selenium.find_element_by_id('id_name')
|
||||||
name.send_keys(domain_name)
|
name.send_keys(domain_name)
|
||||||
|
|
||||||
account_input = self.selenium.find_element_by_id('id_account')
|
account_input = self.selenium.find_element_by_id('id_account')
|
||||||
account_select = Select(account_input)
|
account_select = Select(account_input)
|
||||||
account_select.select_by_value(str(self.account.pk))
|
account_select.select_by_value(str(self.account.pk))
|
||||||
|
|
||||||
value_input = self._add_records(records)
|
value_input = self._add_records(records)
|
||||||
value_input.submit()
|
value_input.submit()
|
||||||
self.assertNotEqual(url, self.selenium.current_url)
|
self.assertNotEqual(url, self.selenium.current_url)
|
||||||
|
|
||||||
@snapshot_on_error
|
@snapshot_on_error
|
||||||
def delete(self, domain_name):
|
def delete(self, domain_name):
|
||||||
domain = Domain.objects.get(name=domain_name)
|
domain = Domain.objects.get(name=domain_name)
|
||||||
self.admin_delete(domain)
|
self.admin_delete(domain)
|
||||||
|
|
||||||
@snapshot_on_error
|
@snapshot_on_error
|
||||||
def update(self, domain_name, records):
|
def update(self, domain_name, records):
|
||||||
domain = Domain.objects.get(name=domain_name)
|
domain = Domain.objects.get(name=domain_name)
|
||||||
|
@ -283,18 +283,18 @@ class RESTDomainMixin(DomainTestMixin):
|
||||||
super(RESTDomainMixin, self).setUp()
|
super(RESTDomainMixin, self).setUp()
|
||||||
self.rest_login()
|
self.rest_login()
|
||||||
self.add_route()
|
self.add_route()
|
||||||
|
|
||||||
@save_response_on_error
|
@save_response_on_error
|
||||||
def add(self, domain_name, records):
|
def add(self, domain_name, records):
|
||||||
records = [ dict(type=type, value=value) for type,value in records ]
|
records = [ dict(type=type, value=value) for type,value in records ]
|
||||||
self.rest.domains.create(name=domain_name, records=records)
|
self.rest.domains.create(name=domain_name, records=records)
|
||||||
|
|
||||||
@save_response_on_error
|
@save_response_on_error
|
||||||
def delete(self, domain_name):
|
def delete(self, domain_name):
|
||||||
domain = Domain.objects.get(name=domain_name)
|
domain = Domain.objects.get(name=domain_name)
|
||||||
domain = self.rest.domains.retrieve(id=domain.pk)
|
domain = self.rest.domains.retrieve(id=domain.pk)
|
||||||
domain.delete()
|
domain.delete()
|
||||||
|
|
||||||
@save_response_on_error
|
@save_response_on_error
|
||||||
def update(self, domain_name, records):
|
def update(self, domain_name, records):
|
||||||
records = [ dict(type=type, value=value) for type,value in records ]
|
records = [ dict(type=type, value=value) for type,value in records ]
|
||||||
|
@ -307,7 +307,7 @@ class Bind9BackendMixin(object):
|
||||||
DEPENDENCIES = (
|
DEPENDENCIES = (
|
||||||
'orchestra.contrib.orchestration',
|
'orchestra.contrib.orchestration',
|
||||||
)
|
)
|
||||||
|
|
||||||
def add_route(self):
|
def add_route(self):
|
||||||
master = Server.objects.create(name=self.MASTER_SERVER, address=self.MASTER_SERVER_ADDR)
|
master = Server.objects.create(name=self.MASTER_SERVER, address=self.MASTER_SERVER_ADDR)
|
||||||
backend = backends.Bind9MasterDomainController.get_name()
|
backend = backends.Bind9MasterDomainController.get_name()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
|
from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.contrib.admin.utils import unquote
|
from django.contrib.admin.utils import unquote
|
||||||
|
@ -30,10 +30,10 @@ class LogEntryAdmin(admin.ModelAdmin):
|
||||||
actions = None
|
actions = None
|
||||||
list_select_related = ('user', 'content_type')
|
list_select_related = ('user', 'content_type')
|
||||||
list_display_links = None
|
list_display_links = None
|
||||||
|
|
||||||
user_link = admin_link('user')
|
user_link = admin_link('user')
|
||||||
display_action_time = admin_date('action_time', short_description=_("Time"))
|
display_action_time = admin_date('action_time', short_description=_("Time"))
|
||||||
|
|
||||||
def display_message(self, log):
|
def display_message(self, log):
|
||||||
edit = '<a href="%(url)s"><img src="%(img)s"></img></a>' % {
|
edit = '<a href="%(url)s"><img src="%(img)s"></img></a>' % {
|
||||||
'url': reverse('admin:admin_logentry_change', args=(log.pk,)),
|
'url': reverse('admin:admin_logentry_change', args=(log.pk,)),
|
||||||
|
@ -58,7 +58,7 @@ class LogEntryAdmin(admin.ModelAdmin):
|
||||||
display_message.short_description = _("Message")
|
display_message.short_description = _("Message")
|
||||||
display_message.admin_order_field = 'action_flag'
|
display_message.admin_order_field = 'action_flag'
|
||||||
display_message.allow_tags = True
|
display_message.allow_tags = True
|
||||||
|
|
||||||
def display_action(self, log):
|
def display_action(self, log):
|
||||||
if log.is_addition():
|
if log.is_addition():
|
||||||
return _("Added")
|
return _("Added")
|
||||||
|
@ -67,7 +67,7 @@ class LogEntryAdmin(admin.ModelAdmin):
|
||||||
return _("Deleted")
|
return _("Deleted")
|
||||||
display_action.short_description = _("Action")
|
display_action.short_description = _("Action")
|
||||||
display_action.admin_order_field = 'action_flag'
|
display_action.admin_order_field = 'action_flag'
|
||||||
|
|
||||||
def content_object_link(self, log):
|
def content_object_link(self, log):
|
||||||
ct = log.content_type
|
ct = log.content_type
|
||||||
view = 'admin:%s_%s_change' % (ct.app_label, ct.model)
|
view = 'admin:%s_%s_change' % (ct.app_label, ct.model)
|
||||||
|
@ -79,7 +79,7 @@ class LogEntryAdmin(admin.ModelAdmin):
|
||||||
content_object_link.short_description = _("Content object")
|
content_object_link.short_description = _("Content object")
|
||||||
content_object_link.admin_order_field = 'object_repr'
|
content_object_link.admin_order_field = 'object_repr'
|
||||||
content_object_link.allow_tags = True
|
content_object_link.allow_tags = True
|
||||||
|
|
||||||
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
|
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
|
||||||
""" Add rel_opts and object to context """
|
""" Add rel_opts and object to context """
|
||||||
if not add and 'edit' in request.GET.urlencode():
|
if not add and 'edit' in request.GET.urlencode():
|
||||||
|
@ -89,14 +89,14 @@ class LogEntryAdmin(admin.ModelAdmin):
|
||||||
})
|
})
|
||||||
return super(LogEntryAdmin, self).render_change_form(
|
return super(LogEntryAdmin, self).render_change_form(
|
||||||
request, context, add, change, form_url, obj)
|
request, context, add, change, form_url, obj)
|
||||||
|
|
||||||
def response_change(self, request, obj):
|
def response_change(self, request, obj):
|
||||||
""" save and continue preserve edit query string """
|
""" save and continue preserve edit query string """
|
||||||
response = super(LogEntryAdmin, self).response_change(request, obj)
|
response = super(LogEntryAdmin, self).response_change(request, obj)
|
||||||
if 'edit' in request.GET.urlencode() and 'edit' not in response.url:
|
if 'edit' in request.GET.urlencode() and 'edit' not in response.url:
|
||||||
return HttpResponseRedirect(response.url + '?edit=True')
|
return HttpResponseRedirect(response.url + '?edit=True')
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def response_post_save_change(self, request, obj):
|
def response_post_save_change(self, request, obj):
|
||||||
""" save redirect to object history """
|
""" save redirect to object history """
|
||||||
if 'edit' in request.GET.urlencode():
|
if 'edit' in request.GET.urlencode():
|
||||||
|
@ -109,19 +109,19 @@ class LogEntryAdmin(admin.ModelAdmin):
|
||||||
}, post_url)
|
}, post_url)
|
||||||
return HttpResponseRedirect(post_url)
|
return HttpResponseRedirect(post_url)
|
||||||
return super(LogEntryAdmin, self).response_post_save_change(request, obj)
|
return super(LogEntryAdmin, self).response_post_save_change(request, obj)
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def has_delete_permission(self, *args, **kwargs):
|
def has_delete_permission(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def log_addition(self, *args, **kwargs):
|
def log_addition(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def log_change(self, *args, **kwargs):
|
def log_change(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def log_deletion(self, *args, **kwargs):
|
def log_deletion(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
@ -21,14 +21,14 @@ from .helpers import get_ticket_changes, markdown_formated_changes, filter_actio
|
||||||
from .models import Ticket, Queue, Message
|
from .models import Ticket, Queue, Message
|
||||||
|
|
||||||
|
|
||||||
PRIORITY_COLORS = {
|
PRIORITY_COLORS = {
|
||||||
Ticket.HIGH: 'red',
|
Ticket.HIGH: 'red',
|
||||||
Ticket.MEDIUM: 'darkorange',
|
Ticket.MEDIUM: 'darkorange',
|
||||||
Ticket.LOW: 'green',
|
Ticket.LOW: 'green',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
STATE_COLORS = {
|
STATE_COLORS = {
|
||||||
Ticket.NEW: 'grey',
|
Ticket.NEW: 'grey',
|
||||||
Ticket.IN_PROGRESS: 'darkorange',
|
Ticket.IN_PROGRESS: 'darkorange',
|
||||||
Ticket.FEEDBACK: 'purple',
|
Ticket.FEEDBACK: 'purple',
|
||||||
|
@ -44,12 +44,12 @@ class MessageReadOnlyInline(admin.TabularInline):
|
||||||
can_delete = False
|
can_delete = False
|
||||||
fields = ('content_html',)
|
fields = ('content_html',)
|
||||||
readonly_fields = ('content_html',)
|
readonly_fields = ('content_html',)
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
'all': ('orchestra/css/hide-inline-id.css',)
|
'all': ('orchestra/css/hide-inline-id.css',)
|
||||||
}
|
}
|
||||||
|
|
||||||
def content_html(self, msg):
|
def content_html(self, msg):
|
||||||
context = {
|
context = {
|
||||||
'number': msg.number,
|
'number': msg.number,
|
||||||
|
@ -64,10 +64,10 @@ class MessageReadOnlyInline(admin.TabularInline):
|
||||||
return header + content
|
return header + content
|
||||||
content_html.short_description = _("Content")
|
content_html.short_description = _("Content")
|
||||||
content_html.allow_tags = True
|
content_html.allow_tags = True
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def has_delete_permission(self, request, obj=None):
|
def has_delete_permission(self, request, obj=None):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -79,12 +79,12 @@ class MessageInline(admin.TabularInline):
|
||||||
form = MessageInlineForm
|
form = MessageInlineForm
|
||||||
can_delete = False
|
can_delete = False
|
||||||
fields = ('content',)
|
fields = ('content',)
|
||||||
|
|
||||||
def get_formset(self, request, obj=None, **kwargs):
|
def get_formset(self, request, obj=None, **kwargs):
|
||||||
""" hook request.user on the inline form """
|
""" hook request.user on the inline form """
|
||||||
self.form.user = request.user
|
self.form.user = request.user
|
||||||
return super(MessageInline, self).get_formset(request, obj, **kwargs)
|
return super(MessageInline, self).get_formset(request, obj, **kwargs)
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
""" Don't show any message """
|
""" Don't show any message """
|
||||||
qs = super(MessageInline, self).get_queryset(request)
|
qs = super(MessageInline, self).get_queryset(request)
|
||||||
|
@ -103,14 +103,14 @@ class TicketInline(admin.TabularInline):
|
||||||
model = Ticket
|
model = Ticket
|
||||||
extra = 0
|
extra = 0
|
||||||
max_num = 0
|
max_num = 0
|
||||||
|
|
||||||
creator_link = admin_link('creator')
|
creator_link = admin_link('creator')
|
||||||
owner_link = admin_link('owner')
|
owner_link = admin_link('owner')
|
||||||
created = admin_link('created_at')
|
created = admin_link('created_at')
|
||||||
updated = admin_link('updated_at')
|
updated = admin_link('updated_at')
|
||||||
colored_state = admin_colored('state', colors=STATE_COLORS, bold=False)
|
colored_state = admin_colored('state', colors=STATE_COLORS, bold=False)
|
||||||
colored_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False)
|
colored_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False)
|
||||||
|
|
||||||
def ticket_id(self, instance):
|
def ticket_id(self, instance):
|
||||||
return '<b>%s</b>' % admin_link()(instance)
|
return '<b>%s</b>' % admin_link()(instance)
|
||||||
ticket_id.short_description = '#'
|
ticket_id.short_description = '#'
|
||||||
|
@ -176,7 +176,7 @@ class TicketAdmin(ExtendedModelAdmin):
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
list_select_related = ('queue', 'owner', 'creator')
|
list_select_related = ('queue', 'owner', 'creator')
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
'all': ('issues/css/ticket-admin.css',)
|
'all': ('issues/css/ticket-admin.css',)
|
||||||
|
@ -184,14 +184,14 @@ class TicketAdmin(ExtendedModelAdmin):
|
||||||
js = (
|
js = (
|
||||||
'issues/js/ticket-admin.js',
|
'issues/js/ticket-admin.js',
|
||||||
)
|
)
|
||||||
|
|
||||||
display_creator = admin_link('creator')
|
display_creator = admin_link('creator')
|
||||||
display_queue = admin_link('queue')
|
display_queue = admin_link('queue')
|
||||||
display_owner = admin_link('owner')
|
display_owner = admin_link('owner')
|
||||||
updated = admin_date('updated_at')
|
updated = admin_date('updated_at')
|
||||||
display_state = admin_colored('state', colors=STATE_COLORS, bold=False)
|
display_state = admin_colored('state', colors=STATE_COLORS, bold=False)
|
||||||
display_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False)
|
display_priority = admin_colored('priority', colors=PRIORITY_COLORS, bold=False)
|
||||||
|
|
||||||
def display_summary(self, ticket):
|
def display_summary(self, ticket):
|
||||||
context = {
|
context = {
|
||||||
'creator': admin_link('creator')(self, ticket) if ticket.creator else ticket.creator_name,
|
'creator': admin_link('creator')(self, ticket) if ticket.creator else ticket.creator_name,
|
||||||
|
@ -208,7 +208,7 @@ class TicketAdmin(ExtendedModelAdmin):
|
||||||
return '<h4>Added by %(creator)s about %(created)s%(updated)s</h4>' % context
|
return '<h4>Added by %(creator)s about %(created)s%(updated)s</h4>' % context
|
||||||
display_summary.short_description = 'Summary'
|
display_summary.short_description = 'Summary'
|
||||||
display_summary.allow_tags = True
|
display_summary.allow_tags = True
|
||||||
|
|
||||||
def unbold_id(self, ticket):
|
def unbold_id(self, ticket):
|
||||||
""" Unbold id if ticket is read """
|
""" Unbold id if ticket is read """
|
||||||
if ticket.is_read_by(self.user):
|
if ticket.is_read_by(self.user):
|
||||||
|
@ -217,7 +217,7 @@ class TicketAdmin(ExtendedModelAdmin):
|
||||||
unbold_id.allow_tags = True
|
unbold_id.allow_tags = True
|
||||||
unbold_id.short_description = "#"
|
unbold_id.short_description = "#"
|
||||||
unbold_id.admin_order_field = 'id'
|
unbold_id.admin_order_field = 'id'
|
||||||
|
|
||||||
def bold_subject(self, ticket):
|
def bold_subject(self, ticket):
|
||||||
""" Bold subject when tickets are unread for request.user """
|
""" Bold subject when tickets are unread for request.user """
|
||||||
if ticket.is_read_by(self.user):
|
if ticket.is_read_by(self.user):
|
||||||
|
@ -226,31 +226,31 @@ class TicketAdmin(ExtendedModelAdmin):
|
||||||
bold_subject.allow_tags = True
|
bold_subject.allow_tags = True
|
||||||
bold_subject.short_description = _("Subject")
|
bold_subject.short_description = _("Subject")
|
||||||
bold_subject.admin_order_field = 'subject'
|
bold_subject.admin_order_field = 'subject'
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
if db_field.name == 'subject':
|
if db_field.name == 'subject':
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'120'})
|
kwargs['widget'] = forms.TextInput(attrs={'size':'120'})
|
||||||
return super(TicketAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(TicketAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
def save_model(self, request, obj, *args, **kwargs):
|
def save_model(self, request, obj, *args, **kwargs):
|
||||||
""" Define creator for new tickets """
|
""" Define creator for new tickets """
|
||||||
if not obj.pk:
|
if not obj.pk:
|
||||||
obj.creator = request.user
|
obj.creator = request.user
|
||||||
super(TicketAdmin, self).save_model(request, obj, *args, **kwargs)
|
super(TicketAdmin, self).save_model(request, obj, *args, **kwargs)
|
||||||
obj.mark_as_read_by(request.user)
|
obj.mark_as_read_by(request.user)
|
||||||
|
|
||||||
def get_urls(self):
|
def get_urls(self):
|
||||||
""" add markdown preview url """
|
""" add markdown preview url """
|
||||||
return [
|
return [
|
||||||
url(r'^preview/$',
|
url(r'^preview/$',
|
||||||
wrap_admin_view(self, self.message_preview_view))
|
wrap_admin_view(self, self.message_preview_view))
|
||||||
] + super(TicketAdmin, self).get_urls()
|
] + super(TicketAdmin, self).get_urls()
|
||||||
|
|
||||||
def add_view(self, request, form_url='', extra_context=None):
|
def add_view(self, request, form_url='', extra_context=None):
|
||||||
""" Do not sow message inlines """
|
""" Do not sow message inlines """
|
||||||
return super(TicketAdmin, self).add_view(request, form_url, extra_context)
|
return super(TicketAdmin, self).add_view(request, form_url, extra_context)
|
||||||
|
|
||||||
def change_view(self, request, object_id, form_url='', extra_context=None):
|
def change_view(self, request, object_id, form_url='', extra_context=None):
|
||||||
""" Change view actions based on ticket state """
|
""" Change view actions based on ticket state """
|
||||||
ticket = get_object_or_404(Ticket, pk=object_id)
|
ticket = get_object_or_404(Ticket, pk=object_id)
|
||||||
|
@ -269,12 +269,12 @@ class TicketAdmin(ExtendedModelAdmin):
|
||||||
context.update(extra_context or {})
|
context.update(extra_context or {})
|
||||||
return super(TicketAdmin, self).change_view(request, object_id, form_url=form_url,
|
return super(TicketAdmin, self).change_view(request, object_id, form_url=form_url,
|
||||||
extra_context=context)
|
extra_context=context)
|
||||||
|
|
||||||
def changelist_view(self, request, extra_context=None):
|
def changelist_view(self, request, extra_context=None):
|
||||||
# Hook user for bold_subject
|
# Hook user for bold_subject
|
||||||
self.user = request.user
|
self.user = request.user
|
||||||
return super(TicketAdmin,self).changelist_view(request, extra_context=extra_context)
|
return super(TicketAdmin,self).changelist_view(request, extra_context=extra_context)
|
||||||
|
|
||||||
def message_preview_view(self, request):
|
def message_preview_view(self, request):
|
||||||
""" markdown preview render via ajax """
|
""" markdown preview render via ajax """
|
||||||
data = request.POST.get("data")
|
data = request.POST.get("data")
|
||||||
|
@ -287,12 +287,12 @@ class QueueAdmin(admin.ModelAdmin):
|
||||||
actions = (set_default_queue,)
|
actions = (set_default_queue,)
|
||||||
inlines = (TicketInline,)
|
inlines = (TicketInline,)
|
||||||
ordering = ('name',)
|
ordering = ('name',)
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
'all': ('orchestra/css/hide-inline-id.css',)
|
'all': ('orchestra/css/hide-inline-id.css',)
|
||||||
}
|
}
|
||||||
|
|
||||||
def num_tickets(self, queue):
|
def num_tickets(self, queue):
|
||||||
num = queue.tickets__count
|
num = queue.tickets__count
|
||||||
url = reverse('admin:issues_ticket_changelist')
|
url = reverse('admin:issues_ticket_changelist')
|
||||||
|
@ -301,7 +301,7 @@ class QueueAdmin(admin.ModelAdmin):
|
||||||
num_tickets.short_description = _("Tickets")
|
num_tickets.short_description = _("Tickets")
|
||||||
num_tickets.admin_order_field = 'tickets__count'
|
num_tickets.admin_order_field = 'tickets__count'
|
||||||
num_tickets.allow_tags = True
|
num_tickets.allow_tags = True
|
||||||
|
|
||||||
def get_list_display(self, request):
|
def get_list_display(self, request):
|
||||||
""" show notifications """
|
""" show notifications """
|
||||||
list_display = list(self.list_display)
|
list_display = list(self.list_display)
|
||||||
|
@ -312,7 +312,7 @@ class QueueAdmin(admin.ModelAdmin):
|
||||||
display_notify.boolean = True
|
display_notify.boolean = True
|
||||||
list_display.append(display_notify)
|
list_display.append(display_notify)
|
||||||
return list_display
|
return list_display
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super(QueueAdmin, self).get_queryset(request)
|
qs = super(QueueAdmin, self).get_queryset(request)
|
||||||
qs = qs.annotate(models.Count('tickets'))
|
qs = qs.annotate(models.Count('tickets'))
|
||||||
|
|
|
@ -7,7 +7,7 @@ from email.mime.text import MIMEText
|
||||||
import requests
|
import requests
|
||||||
from django.conf import settings as djsettings
|
from django.conf import settings as djsettings
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from orchestra.admin.utils import change_url
|
from orchestra.admin.utils import change_url
|
||||||
from orchestra.contrib.domains.models import Domain
|
from orchestra.contrib.domains.models import Domain
|
||||||
from orchestra.contrib.orchestration.models import Route, Server
|
from orchestra.contrib.orchestration.models import Route, Server
|
||||||
|
|
|
@ -3,7 +3,7 @@ from urllib.parse import parse_qs
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import F, Count, Value as V
|
from django.db.models import F, Count, Value as V
|
||||||
from django.db.models.functions import Concat
|
from django.db.models.functions import Concat
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
@ -28,7 +28,7 @@ from .widgets import OpenCustomFilteringOnSelect
|
||||||
class AutoresponseInline(admin.StackedInline):
|
class AutoresponseInline(admin.StackedInline):
|
||||||
model = Autoresponse
|
model = Autoresponse
|
||||||
verbose_name_plural = _("autoresponse")
|
verbose_name_plural = _("autoresponse")
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
if db_field.name == 'subject':
|
if db_field.name == 'subject':
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'118'})
|
kwargs['widget'] = forms.TextInput(attrs={'size':'118'})
|
||||||
|
@ -76,12 +76,12 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
||||||
form = MailboxChangeForm
|
form = MailboxChangeForm
|
||||||
list_prefetch_related = ('addresses__domain',)
|
list_prefetch_related = ('addresses__domain',)
|
||||||
actions = (disable, enable, list_accounts)
|
actions = (disable, enable, list_accounts)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(MailboxAdmin, self).__init__(*args, **kwargs)
|
super(MailboxAdmin, self).__init__(*args, **kwargs)
|
||||||
if settings.MAILBOXES_LOCAL_DOMAIN:
|
if settings.MAILBOXES_LOCAL_DOMAIN:
|
||||||
type(self).actions = self.actions + (SendMailboxEmail(),)
|
type(self).actions = self.actions + (SendMailboxEmail(),)
|
||||||
|
|
||||||
def display_addresses(self, mailbox):
|
def display_addresses(self, mailbox):
|
||||||
# Get from forwards
|
# Get from forwards
|
||||||
cache = caches.get_request_cache()
|
cache = caches.get_request_cache()
|
||||||
|
@ -111,7 +111,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
||||||
return '<br>'.join(addresses+forwards)
|
return '<br>'.join(addresses+forwards)
|
||||||
display_addresses.short_description = _("Addresses")
|
display_addresses.short_description = _("Addresses")
|
||||||
display_addresses.allow_tags = True
|
display_addresses.allow_tags = True
|
||||||
|
|
||||||
def display_forwards(self, mailbox):
|
def display_forwards(self, mailbox):
|
||||||
forwards = []
|
forwards = []
|
||||||
for addr in mailbox.get_forwards():
|
for addr in mailbox.get_forwards():
|
||||||
|
@ -120,19 +120,19 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
||||||
return '<br>'.join(forwards)
|
return '<br>'.join(forwards)
|
||||||
display_forwards.short_description = _("Forward from")
|
display_forwards.short_description = _("Forward from")
|
||||||
display_forwards.allow_tags = True
|
display_forwards.allow_tags = True
|
||||||
|
|
||||||
def display_filtering(self, mailbox):
|
def display_filtering(self, mailbox):
|
||||||
""" becacuse of allow_tags = True """
|
""" becacuse of allow_tags = True """
|
||||||
return mailbox.get_filtering_display()
|
return mailbox.get_filtering_display()
|
||||||
display_filtering.short_description = _("Filtering")
|
display_filtering.short_description = _("Filtering")
|
||||||
display_filtering.admin_order_field = 'filtering'
|
display_filtering.admin_order_field = 'filtering'
|
||||||
display_filtering.allow_tags = True
|
display_filtering.allow_tags = True
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
if db_field.name == 'filtering':
|
if db_field.name == 'filtering':
|
||||||
kwargs['widget'] = OpenCustomFilteringOnSelect()
|
kwargs['widget'] = OpenCustomFilteringOnSelect()
|
||||||
return super(MailboxAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(MailboxAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
def get_fieldsets(self, request, obj=None):
|
def get_fieldsets(self, request, obj=None):
|
||||||
fieldsets = super(MailboxAdmin, self).get_fieldsets(request, obj)
|
fieldsets = super(MailboxAdmin, self).get_fieldsets(request, obj)
|
||||||
if obj and obj.filtering == obj.CUSTOM:
|
if obj and obj.filtering == obj.CUSTOM:
|
||||||
|
@ -144,31 +144,31 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
||||||
fieldsets = list(copy.deepcopy(fieldsets))
|
fieldsets = list(copy.deepcopy(fieldsets))
|
||||||
fieldsets.pop(-1)
|
fieldsets.pop(-1)
|
||||||
return fieldsets
|
return fieldsets
|
||||||
|
|
||||||
def get_form(self, *args, **kwargs):
|
def get_form(self, *args, **kwargs):
|
||||||
form = super(MailboxAdmin, self).get_form(*args, **kwargs)
|
form = super(MailboxAdmin, self).get_form(*args, **kwargs)
|
||||||
form.modeladmin = self
|
form.modeladmin = self
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def get_search_results(self, request, queryset, search_term):
|
def get_search_results(self, request, queryset, search_term):
|
||||||
# Remove local domain from the search term if present (implicit local addreç)
|
# Remove local domain from the search term if present (implicit local addreç)
|
||||||
search_term = search_term.replace('@'+settings.MAILBOXES_LOCAL_DOMAIN, '')
|
search_term = search_term.replace('@'+settings.MAILBOXES_LOCAL_DOMAIN, '')
|
||||||
# Split address name from domain in order to support address searching
|
# Split address name from domain in order to support address searching
|
||||||
search_term = search_term.replace('@', ' ')
|
search_term = search_term.replace('@', ' ')
|
||||||
return super(MailboxAdmin, self).get_search_results(request, queryset, search_term)
|
return super(MailboxAdmin, self).get_search_results(request, queryset, search_term)
|
||||||
|
|
||||||
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
|
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
|
||||||
if not add:
|
if not add:
|
||||||
self.check_unrelated_address(request, obj)
|
self.check_unrelated_address(request, obj)
|
||||||
self.check_matching_address(request, obj)
|
self.check_matching_address(request, obj)
|
||||||
return super(MailboxAdmin, self).render_change_form(
|
return super(MailboxAdmin, self).render_change_form(
|
||||||
request, context, add, change, form_url, obj)
|
request, context, add, change, form_url, obj)
|
||||||
|
|
||||||
def log_addition(self, request, object, *args, **kwargs):
|
def log_addition(self, request, object, *args, **kwargs):
|
||||||
self.check_unrelated_address(request, object)
|
self.check_unrelated_address(request, object)
|
||||||
self.check_matching_address(request, object)
|
self.check_matching_address(request, object)
|
||||||
return super(MailboxAdmin, self).log_addition(request, object, *args, **kwargs)
|
return super(MailboxAdmin, self).log_addition(request, object, *args, **kwargs)
|
||||||
|
|
||||||
def check_matching_address(self, request, obj):
|
def check_matching_address(self, request, obj):
|
||||||
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
|
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
|
||||||
if obj.name and local_domain:
|
if obj.name and local_domain:
|
||||||
|
@ -183,7 +183,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
||||||
"selecting it makes sense.") % (obj, addr)
|
"selecting it makes sense.") % (obj, addr)
|
||||||
if msg not in (m.message for m in messages.get_messages(request)):
|
if msg not in (m.message for m in messages.get_messages(request)):
|
||||||
self.message_user(request, msg, level=messages.WARNING)
|
self.message_user(request, msg, level=messages.WARNING)
|
||||||
|
|
||||||
def check_unrelated_address(self, request, obj):
|
def check_unrelated_address(self, request, obj):
|
||||||
# Check if there exists an unrelated local Address for this mbox
|
# Check if there exists an unrelated local Address for this mbox
|
||||||
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
|
local_domain = settings.MAILBOXES_LOCAL_DOMAIN
|
||||||
|
@ -204,7 +204,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
||||||
# Prevent duplication (add_view+continue)
|
# Prevent duplication (add_view+continue)
|
||||||
if msg not in (m.message for m in messages.get_messages(request)):
|
if msg not in (m.message for m in messages.get_messages(request)):
|
||||||
self.message_user(request, msg, level=messages.WARNING)
|
self.message_user(request, msg, level=messages.WARNING)
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
""" save hacky mailbox.addresses and local domain clashing """
|
""" save hacky mailbox.addresses and local domain clashing """
|
||||||
if obj.filtering != obj.CUSTOM:
|
if obj.filtering != obj.CUSTOM:
|
||||||
|
@ -237,20 +237,20 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
filter_horizontal = ['mailboxes']
|
filter_horizontal = ['mailboxes']
|
||||||
form = AddressForm
|
form = AddressForm
|
||||||
list_prefetch_related = ('mailboxes', 'domain')
|
list_prefetch_related = ('mailboxes', 'domain')
|
||||||
|
|
||||||
domain_link = admin_link('domain', order='domain__name')
|
domain_link = admin_link('domain', order='domain__name')
|
||||||
|
|
||||||
def display_email(self, address):
|
def display_email(self, address):
|
||||||
return address.computed_email
|
return address.computed_email
|
||||||
display_email.short_description = _("Email")
|
display_email.short_description = _("Email")
|
||||||
display_email.admin_order_field = 'computed_email'
|
display_email.admin_order_field = 'computed_email'
|
||||||
|
|
||||||
def email_link(self, address):
|
def email_link(self, address):
|
||||||
link = self.domain_link(address)
|
link = self.domain_link(address)
|
||||||
return "%s@%s" % (address.name, link)
|
return "%s@%s" % (address.name, link)
|
||||||
email_link.short_description = _("Email")
|
email_link.short_description = _("Email")
|
||||||
email_link.allow_tags = True
|
email_link.allow_tags = True
|
||||||
|
|
||||||
def display_mailboxes(self, address):
|
def display_mailboxes(self, address):
|
||||||
boxes = []
|
boxes = []
|
||||||
for mailbox in address.mailboxes.all():
|
for mailbox in address.mailboxes.all():
|
||||||
|
@ -260,7 +260,7 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
display_mailboxes.short_description = _("Mailboxes")
|
display_mailboxes.short_description = _("Mailboxes")
|
||||||
display_mailboxes.allow_tags = True
|
display_mailboxes.allow_tags = True
|
||||||
display_mailboxes.admin_order_field = 'mailboxes__count'
|
display_mailboxes.admin_order_field = 'mailboxes__count'
|
||||||
|
|
||||||
def display_all_mailboxes(self, address):
|
def display_all_mailboxes(self, address):
|
||||||
boxes = []
|
boxes = []
|
||||||
for mailbox in address.get_mailboxes():
|
for mailbox in address.get_mailboxes():
|
||||||
|
@ -269,7 +269,7 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
return '<br>'.join(boxes)
|
return '<br>'.join(boxes)
|
||||||
display_all_mailboxes.short_description = _("Mailboxes links")
|
display_all_mailboxes.short_description = _("Mailboxes links")
|
||||||
display_all_mailboxes.allow_tags = True
|
display_all_mailboxes.allow_tags = True
|
||||||
|
|
||||||
def display_forward(self, address):
|
def display_forward(self, address):
|
||||||
forward_mailboxes = {m.name: m for m in address.get_forward_mailboxes()}
|
forward_mailboxes = {m.name: m for m in address.get_forward_mailboxes()}
|
||||||
values = []
|
values = []
|
||||||
|
@ -283,12 +283,12 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
display_forward.short_description = _("Forward")
|
display_forward.short_description = _("Forward")
|
||||||
display_forward.allow_tags = True
|
display_forward.allow_tags = True
|
||||||
display_forward.admin_order_field = 'forward'
|
display_forward.admin_order_field = 'forward'
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
if db_field.name == 'forward':
|
if db_field.name == 'forward':
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'118'})
|
kwargs['widget'] = forms.TextInput(attrs={'size':'118'})
|
||||||
return super(AddressAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(AddressAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
def get_fields(self, request, obj=None):
|
def get_fields(self, request, obj=None):
|
||||||
""" Remove mailboxes field when creating address from a popup i.e. from mailbox add form """
|
""" Remove mailboxes field when creating address from a popup i.e. from mailbox add form """
|
||||||
fields = super(AddressAdmin, self).get_fields(request, obj)
|
fields = super(AddressAdmin, self).get_fields(request, obj)
|
||||||
|
@ -297,22 +297,22 @@ class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
fields = list(fields)
|
fields = list(fields)
|
||||||
fields.remove('mailboxes')
|
fields.remove('mailboxes')
|
||||||
return fields
|
return fields
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super(AddressAdmin, self).get_queryset(request)
|
qs = super(AddressAdmin, self).get_queryset(request)
|
||||||
qs = qs.annotate(computed_email=Concat(F('name'), V('@'), F('domain__name')))
|
qs = qs.annotate(computed_email=Concat(F('name'), V('@'), F('domain__name')))
|
||||||
return qs.annotate(Count('mailboxes'))
|
return qs.annotate(Count('mailboxes'))
|
||||||
|
|
||||||
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
|
def render_change_form(self, request, context, add=False, change=False, form_url='', obj=None):
|
||||||
if not add:
|
if not add:
|
||||||
self.check_matching_mailbox(request, obj)
|
self.check_matching_mailbox(request, obj)
|
||||||
return super(AddressAdmin, self).render_change_form(
|
return super(AddressAdmin, self).render_change_form(
|
||||||
request, context, add, change, form_url, obj)
|
request, context, add, change, form_url, obj)
|
||||||
|
|
||||||
def log_addition(self, request, object, *args, **kwargs):
|
def log_addition(self, request, object, *args, **kwargs):
|
||||||
self.check_matching_mailbox(request, object)
|
self.check_matching_mailbox(request, object)
|
||||||
return super(AddressAdmin, self).log_addition(request, object, *args, **kwargs)
|
return super(AddressAdmin, self).log_addition(request, object, *args, **kwargs)
|
||||||
|
|
||||||
def check_matching_mailbox(self, request, obj):
|
def check_matching_mailbox(self, request, obj):
|
||||||
# Check if new addresse matches with a mbox because of having a local domain
|
# Check if new addresse matches with a mbox because of having a local domain
|
||||||
if obj.name and obj.domain and obj.domain.name == settings.MAILBOXES_LOCAL_DOMAIN:
|
if obj.name and obj.domain and obj.domain.name == settings.MAILBOXES_LOCAL_DOMAIN:
|
||||||
|
|
|
@ -11,7 +11,7 @@ from django.apps import apps
|
||||||
from django.conf import settings as djsettings
|
from django.conf import settings as djsettings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from selenium.webdriver.support.select import Select
|
from selenium.webdriver.support.select import Select
|
||||||
|
|
||||||
from orchestra.contrib.orchestration.models import Server, Route
|
from orchestra.contrib.orchestration.models import Server, Route
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ import email
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -52,11 +52,11 @@ class MessageAdmin(ExtendedModelAdmin):
|
||||||
)
|
)
|
||||||
date_hierarchy = 'created_at'
|
date_hierarchy = 'created_at'
|
||||||
change_view_actions = (last,)
|
change_view_actions = (last,)
|
||||||
|
|
||||||
colored_state = admin_colored('state', colors=COLORS)
|
colored_state = admin_colored('state', colors=COLORS)
|
||||||
created_at_delta = admin_date('created_at')
|
created_at_delta = admin_date('created_at')
|
||||||
last_try_delta = admin_date('last_try')
|
last_try_delta = admin_date('last_try')
|
||||||
|
|
||||||
def display_subject(self, instance):
|
def display_subject(self, instance):
|
||||||
subject = instance.subject
|
subject = instance.subject
|
||||||
if len(subject) > 64:
|
if len(subject) > 64:
|
||||||
|
@ -65,7 +65,7 @@ class MessageAdmin(ExtendedModelAdmin):
|
||||||
display_subject.short_description = _("Subject")
|
display_subject.short_description = _("Subject")
|
||||||
display_subject.admin_order_field = 'subject'
|
display_subject.admin_order_field = 'subject'
|
||||||
display_subject.allow_tags = True
|
display_subject.allow_tags = True
|
||||||
|
|
||||||
def display_retries(self, instance):
|
def display_retries(self, instance):
|
||||||
num_logs = instance.logs__count
|
num_logs = instance.logs__count
|
||||||
if num_logs == 1:
|
if num_logs == 1:
|
||||||
|
@ -78,7 +78,7 @@ class MessageAdmin(ExtendedModelAdmin):
|
||||||
display_retries.short_description = _("Retries")
|
display_retries.short_description = _("Retries")
|
||||||
display_retries.admin_order_field = 'retries'
|
display_retries.admin_order_field = 'retries'
|
||||||
display_retries.allow_tags = True
|
display_retries.allow_tags = True
|
||||||
|
|
||||||
def display_content(self, instance):
|
def display_content(self, instance):
|
||||||
part = email.message_from_string(instance.content)
|
part = email.message_from_string(instance.content)
|
||||||
payload = part.get_payload()
|
payload = part.get_payload()
|
||||||
|
@ -102,19 +102,19 @@ class MessageAdmin(ExtendedModelAdmin):
|
||||||
return payload
|
return payload
|
||||||
display_content.short_description = _("Content")
|
display_content.short_description = _("Content")
|
||||||
display_content.allow_tags = True
|
display_content.allow_tags = True
|
||||||
|
|
||||||
def display_full_subject(self, instance):
|
def display_full_subject(self, instance):
|
||||||
return instance.subject
|
return instance.subject
|
||||||
display_full_subject.short_description = _("Subject")
|
display_full_subject.short_description = _("Subject")
|
||||||
|
|
||||||
def display_from(self, instance):
|
def display_from(self, instance):
|
||||||
return instance.from_address
|
return instance.from_address
|
||||||
display_from.short_description = _("From")
|
display_from.short_description = _("From")
|
||||||
|
|
||||||
def display_to(self, instance):
|
def display_to(self, instance):
|
||||||
return instance.to_address
|
return instance.to_address
|
||||||
display_to.short_description = _("To")
|
display_to.short_description = _("To")
|
||||||
|
|
||||||
def get_urls(self):
|
def get_urls(self):
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
urls = super().get_urls()
|
urls = super().get_urls()
|
||||||
|
@ -125,16 +125,16 @@ class MessageAdmin(ExtendedModelAdmin):
|
||||||
name='%s_%s_send_pending' % info)
|
name='%s_%s_send_pending' % info)
|
||||||
)
|
)
|
||||||
return urls
|
return urls
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super().get_queryset(request)
|
qs = super().get_queryset(request)
|
||||||
return qs.annotate(Count('logs')).defer('content')
|
return qs.annotate(Count('logs')).defer('content')
|
||||||
|
|
||||||
def send_pending_view(self, request):
|
def send_pending_view(self, request):
|
||||||
task(send_pending).apply_async()
|
task(send_pending).apply_async()
|
||||||
self.message_user(request, _("Pending messages are being sent on the background."))
|
self.message_user(request, _("Pending messages are being sent on the background."))
|
||||||
return redirect('..')
|
return redirect('..')
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
if db_field.name == 'subject':
|
if db_field.name == 'subject':
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
|
kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
|
||||||
|
@ -148,7 +148,7 @@ class SMTPLogAdmin(admin.ModelAdmin):
|
||||||
list_filter = ('result',)
|
list_filter = ('result',)
|
||||||
fields = ('message_link', 'colored_result', 'date_delta', 'log_message')
|
fields = ('message_link', 'colored_result', 'date_delta', 'log_message')
|
||||||
readonly_fields = fields
|
readonly_fields = fields
|
||||||
|
|
||||||
message_link = admin_link('message')
|
message_link = admin_link('message')
|
||||||
colored_result = admin_colored('result', colors=COLORS, bold=False)
|
colored_result = admin_colored('result', colors=COLORS, bold=False)
|
||||||
date_delta = admin_date('date')
|
date_delta = admin_date('date')
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -36,19 +36,19 @@ class MiscServiceAdmin(ExtendedModelAdmin):
|
||||||
prepopulated_fields = {'name': ('verbose_name',)}
|
prepopulated_fields = {'name': ('verbose_name',)}
|
||||||
change_readonly_fields = ('name',)
|
change_readonly_fields = ('name',)
|
||||||
actions = (disable, enable)
|
actions = (disable, enable)
|
||||||
|
|
||||||
def display_name(self, misc):
|
def display_name(self, misc):
|
||||||
return '<span title="%s">%s</span>' % (misc.description, misc.name)
|
return '<span title="%s">%s</span>' % (misc.description, misc.name)
|
||||||
display_name.short_description = _("name")
|
display_name.short_description = _("name")
|
||||||
display_name.allow_tags = True
|
display_name.allow_tags = True
|
||||||
display_name.admin_order_field = 'name'
|
display_name.admin_order_field = 'name'
|
||||||
|
|
||||||
def display_verbose_name(self, misc):
|
def display_verbose_name(self, misc):
|
||||||
return '<span title="%s">%s</span>' % (misc.description, misc.verbose_name)
|
return '<span title="%s">%s</span>' % (misc.description, misc.verbose_name)
|
||||||
display_verbose_name.short_description = _("verbose name")
|
display_verbose_name.short_description = _("verbose name")
|
||||||
display_verbose_name.allow_tags = True
|
display_verbose_name.allow_tags = True
|
||||||
display_verbose_name.admin_order_field = 'verbose_name'
|
display_verbose_name.admin_order_field = 'verbose_name'
|
||||||
|
|
||||||
def num_instances(self, misc):
|
def num_instances(self, misc):
|
||||||
""" return num slivers as a link to slivers changelist view """
|
""" return num slivers as a link to slivers changelist view """
|
||||||
num = misc.instances__count
|
num = misc.instances__count
|
||||||
|
@ -57,11 +57,11 @@ class MiscServiceAdmin(ExtendedModelAdmin):
|
||||||
return mark_safe('<a href="{0}">{1}</a>'.format(url, num))
|
return mark_safe('<a href="{0}">{1}</a>'.format(url, num))
|
||||||
num_instances.short_description = _("Instances")
|
num_instances.short_description = _("Instances")
|
||||||
num_instances.admin_order_field = 'instances__count'
|
num_instances.admin_order_field = 'instances__count'
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super(MiscServiceAdmin, self).get_queryset(request)
|
qs = super(MiscServiceAdmin, self).get_queryset(request)
|
||||||
return qs.annotate(models.Count('instances', distinct=True))
|
return qs.annotate(models.Count('instances', distinct=True))
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
if db_field.name == 'description':
|
if db_field.name == 'description':
|
||||||
|
@ -83,21 +83,21 @@ class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedMode
|
||||||
actions = (disable, enable)
|
actions = (disable, enable)
|
||||||
plugin_field = 'service'
|
plugin_field = 'service'
|
||||||
plugin = MiscServicePlugin
|
plugin = MiscServicePlugin
|
||||||
|
|
||||||
service_link = admin_link('service')
|
service_link = admin_link('service')
|
||||||
|
|
||||||
def dispaly_active(self, instance):
|
def dispaly_active(self, instance):
|
||||||
return instance.active
|
return instance.active
|
||||||
dispaly_active.short_description = _("Active")
|
dispaly_active.short_description = _("Active")
|
||||||
dispaly_active.boolean = True
|
dispaly_active.boolean = True
|
||||||
dispaly_active.admin_order_field = 'is_active'
|
dispaly_active.admin_order_field = 'is_active'
|
||||||
|
|
||||||
def get_service(self, obj):
|
def get_service(self, obj):
|
||||||
if obj is None:
|
if obj is None:
|
||||||
return self.plugin.get(self.plugin_value).related_instance
|
return self.plugin.get(self.plugin_value).related_instance
|
||||||
else:
|
else:
|
||||||
return obj.service
|
return obj.service
|
||||||
|
|
||||||
def get_fieldsets(self, request, obj=None):
|
def get_fieldsets(self, request, obj=None):
|
||||||
fieldsets = super().get_fieldsets(request, obj)
|
fieldsets = super().get_fieldsets(request, obj)
|
||||||
fields = list(fieldsets[0][1]['fields'])
|
fields = list(fieldsets[0][1]['fields'])
|
||||||
|
@ -110,7 +110,7 @@ class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedMode
|
||||||
fields.insert(2, 'identifier')
|
fields.insert(2, 'identifier')
|
||||||
fieldsets[0][1]['fields'] = fields
|
fieldsets[0][1]['fields'] = fields
|
||||||
return fieldsets
|
return fieldsets
|
||||||
|
|
||||||
def get_form(self, request, obj=None, **kwargs):
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
if obj:
|
if obj:
|
||||||
plugin = self.plugin.get(obj.service.name)()
|
plugin = self.plugin.get(obj.service.name)()
|
||||||
|
@ -127,16 +127,16 @@ class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedMode
|
||||||
validator = import_class(validator_path)
|
validator = import_class(validator_path)
|
||||||
validator(identifier)
|
validator(identifier)
|
||||||
return identifier
|
return identifier
|
||||||
|
|
||||||
form.clean_identifier = clean_identifier
|
form.clean_identifier = clean_identifier
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
if db_field.name == 'description':
|
if db_field.name == 'description':
|
||||||
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4})
|
kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4})
|
||||||
return super(MiscellaneousAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(MiscellaneousAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
if not change:
|
if not change:
|
||||||
plugin = self.plugin
|
plugin = self.plugin
|
||||||
|
|
|
@ -2,7 +2,7 @@ import textwrap
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.mail import mail_admins
|
from django.core.mail import mail_admins
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ungettext, ugettext_lazy as _
|
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from threading import local
|
from threading import local
|
||||||
|
|
||||||
from django.contrib.admin.models import LogEntry
|
from django.contrib.admin.models import LogEntry
|
||||||
from django.core.urlresolvers import resolve
|
from django.urls import resolve
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.db.models.signals import pre_delete, post_save, m2m_changed
|
from django.db.models.signals import pre_delete, post_save, m2m_changed
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
|
@ -39,12 +39,12 @@ class OperationsMiddleware(object):
|
||||||
"""
|
"""
|
||||||
Stores all the operations derived from save and delete signals and executes them
|
Stores all the operations derived from save and delete signals and executes them
|
||||||
at the end of the request/response cycle
|
at the end of the request/response cycle
|
||||||
|
|
||||||
It also works as a transaction middleware, making requets to run within an atomic block.
|
It also works as a transaction middleware, making requets to run within an atomic block.
|
||||||
"""
|
"""
|
||||||
# Thread local is used because request object is not available on model signals
|
# Thread local is used because request object is not available on model signals
|
||||||
thread_locals = local()
|
thread_locals = local()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_pending_operations(cls):
|
def get_pending_operations(cls):
|
||||||
# Check if an error poped up before OperationsMiddleware.process_request()
|
# Check if an error poped up before OperationsMiddleware.process_request()
|
||||||
|
@ -54,7 +54,7 @@ class OperationsMiddleware(object):
|
||||||
request.pending_operations = OrderedSet()
|
request.pending_operations = OrderedSet()
|
||||||
return request.pending_operations
|
return request.pending_operations
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_route_cache(cls):
|
def get_route_cache(cls):
|
||||||
""" chache the routes to save sql queries """
|
""" chache the routes to save sql queries """
|
||||||
|
@ -64,7 +64,7 @@ class OperationsMiddleware(object):
|
||||||
request.route_cache = {}
|
request.route_cache = {}
|
||||||
return request.route_cache
|
return request.route_cache
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def collect(cls, action, **kwargs):
|
def collect(cls, action, **kwargs):
|
||||||
""" Collects all pending operations derived from model signals """
|
""" Collects all pending operations derived from model signals """
|
||||||
|
@ -75,26 +75,26 @@ class OperationsMiddleware(object):
|
||||||
kwargs['route_cache'] = cls.get_route_cache()
|
kwargs['route_cache'] = cls.get_route_cache()
|
||||||
instance = kwargs.pop('instance')
|
instance = kwargs.pop('instance')
|
||||||
manager.collect(instance, action, **kwargs)
|
manager.collect(instance, action, **kwargs)
|
||||||
|
|
||||||
def enter_transaction_management(self):
|
def enter_transaction_management(self):
|
||||||
type(self).thread_locals.transaction = transaction.atomic()
|
type(self).thread_locals.transaction = transaction.atomic()
|
||||||
type(self).thread_locals.transaction.__enter__()
|
type(self).thread_locals.transaction.__enter__()
|
||||||
|
|
||||||
def leave_transaction_management(self, exception=None):
|
def leave_transaction_management(self, exception=None):
|
||||||
locals = type(self).thread_locals
|
locals = type(self).thread_locals
|
||||||
if hasattr(locals, 'transaction'):
|
if hasattr(locals, 'transaction'):
|
||||||
# Don't fucking know why sometimes thread_locals does not contain a transaction
|
# Don't fucking know why sometimes thread_locals does not contain a transaction
|
||||||
locals.transaction.__exit__(exception, None, None)
|
locals.transaction.__exit__(exception, None, None)
|
||||||
|
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
""" Store request on a thread local variable """
|
""" Store request on a thread local variable """
|
||||||
type(self).thread_locals.request = request
|
type(self).thread_locals.request = request
|
||||||
self.enter_transaction_management()
|
self.enter_transaction_management()
|
||||||
|
|
||||||
def process_exception(self, request, exception):
|
def process_exception(self, request, exception):
|
||||||
"""Rolls back the database and leaves transaction management"""
|
"""Rolls back the database and leaves transaction management"""
|
||||||
self.leave_transaction_management(exception)
|
self.leave_transaction_management(exception)
|
||||||
|
|
||||||
def process_response(self, request, response):
|
def process_response(self, request, response):
|
||||||
""" Processes pending backend operations """
|
""" Processes pending backend operations """
|
||||||
if response.status_code != 500:
|
if response.status_code != 500:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
@ -17,7 +17,7 @@ class BillSelectedOrders(object):
|
||||||
verbose_name = _("Bill")
|
verbose_name = _("Bill")
|
||||||
template = 'admin/orders/order/bill_selected_options.html'
|
template = 'admin/orders/order/bill_selected_options.html'
|
||||||
__name__ = 'bill_selected_orders'
|
__name__ = 'bill_selected_orders'
|
||||||
|
|
||||||
def __call__(self, modeladmin, request, queryset):
|
def __call__(self, modeladmin, request, queryset):
|
||||||
""" make this monster behave like a function """
|
""" make this monster behave like a function """
|
||||||
self.modeladmin = modeladmin
|
self.modeladmin = modeladmin
|
||||||
|
@ -34,7 +34,7 @@ class BillSelectedOrders(object):
|
||||||
del(self.queryset)
|
del(self.queryset)
|
||||||
del(self.context)
|
del(self.context)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def set_options(self, request):
|
def set_options(self, request):
|
||||||
form = BillSelectedOptionsForm()
|
form = BillSelectedOptionsForm()
|
||||||
if request.POST.get('step'):
|
if request.POST.get('step'):
|
||||||
|
@ -56,7 +56,7 @@ class BillSelectedOrders(object):
|
||||||
'form': form,
|
'form': form,
|
||||||
})
|
})
|
||||||
return render(request, self.template, self.context)
|
return render(request, self.template, self.context)
|
||||||
|
|
||||||
def select_related(self, request):
|
def select_related(self, request):
|
||||||
# TODO use changelist ?
|
# TODO use changelist ?
|
||||||
related = self.queryset.get_related().select_related('account', 'service')
|
related = self.queryset.get_related().select_related('account', 'service')
|
||||||
|
@ -76,7 +76,7 @@ class BillSelectedOrders(object):
|
||||||
'form': form,
|
'form': form,
|
||||||
})
|
})
|
||||||
return render(request, self.template, self.context)
|
return render(request, self.template, self.context)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def confirmation(self, request):
|
def confirmation(self, request):
|
||||||
form = BillSelectConfirmationForm(initial=self.options)
|
form = BillSelectConfirmationForm(initial=self.options)
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.html import escape
|
from django.utils.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
from orchestra.admin.utils import admin_link, admin_date, change_url
|
from orchestra.admin.utils import admin_link, admin_date, change_url
|
||||||
from orchestra.contrib.accounts.actions import list_accounts
|
from orchestra.contrib.accounts.actions import list_accounts
|
||||||
from orchestra.contrib.accounts.admin import AccountAdminMixin
|
from orchestra.contrib.accounts.admin import AccountAdminMixin
|
||||||
|
@ -22,10 +22,10 @@ class MetricStorageInline(admin.TabularInline):
|
||||||
model = MetricStorage
|
model = MetricStorage
|
||||||
readonly_fields = ('value', 'created_on', 'updated_on')
|
readonly_fields = ('value', 'created_on', 'updated_on')
|
||||||
extra = 0
|
extra = 0
|
||||||
|
|
||||||
def has_add_permission(self, request, obj=None):
|
def has_add_permission(self, request, obj=None):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_fieldsets(self, request, obj=None):
|
def get_fieldsets(self, request, obj=None):
|
||||||
if obj:
|
if obj:
|
||||||
url = reverse('admin:orders_metricstorage_changelist')
|
url = reverse('admin:orders_metricstorage_changelist')
|
||||||
|
@ -33,7 +33,7 @@ class MetricStorageInline(admin.TabularInline):
|
||||||
title = _('Metric storage, last 10 entries, <a href="%s">(See all)</a>')
|
title = _('Metric storage, last 10 entries, <a href="%s">(See all)</a>')
|
||||||
self.verbose_name_plural = mark_safe(title % url)
|
self.verbose_name_plural = mark_safe(title % url)
|
||||||
return super(MetricStorageInline, self).get_fieldsets(request, obj)
|
return super(MetricStorageInline, self).get_fieldsets(request, obj)
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super(MetricStorageInline, self).get_queryset(request)
|
qs = super(MetricStorageInline, self).get_queryset(request)
|
||||||
change_view = bool(self.parent_object and self.parent_object.pk)
|
change_view = bool(self.parent_object and self.parent_object.pk)
|
||||||
|
@ -106,17 +106,17 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
'content_object_repr', 'content_object_link', 'bills_links', 'account_link',
|
'content_object_repr', 'content_object_link', 'bills_links', 'account_link',
|
||||||
'service_link'
|
'service_link'
|
||||||
)
|
)
|
||||||
|
|
||||||
service_link = admin_link('service')
|
service_link = admin_link('service')
|
||||||
display_registered_on = admin_date('registered_on')
|
display_registered_on = admin_date('registered_on')
|
||||||
display_cancelled_on = admin_date('cancelled_on')
|
display_cancelled_on = admin_date('cancelled_on')
|
||||||
|
|
||||||
def display_description(self, order):
|
def display_description(self, order):
|
||||||
return order.description[:64]
|
return order.description[:64]
|
||||||
display_description.short_description = _("Description")
|
display_description.short_description = _("Description")
|
||||||
display_description.allow_tags = True
|
display_description.allow_tags = True
|
||||||
display_description.admin_order_field = 'description'
|
display_description.admin_order_field = 'description'
|
||||||
|
|
||||||
def content_object_link(self, order):
|
def content_object_link(self, order):
|
||||||
if order.content_object:
|
if order.content_object:
|
||||||
try:
|
try:
|
||||||
|
@ -131,7 +131,7 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
content_object_link.short_description = _("Content object")
|
content_object_link.short_description = _("Content object")
|
||||||
content_object_link.allow_tags = True
|
content_object_link.allow_tags = True
|
||||||
content_object_link.admin_order_field = 'content_object_repr'
|
content_object_link.admin_order_field = 'content_object_repr'
|
||||||
|
|
||||||
def bills_links(self, order):
|
def bills_links(self, order):
|
||||||
bills = []
|
bills = []
|
||||||
make_link = admin_link()
|
make_link = admin_link()
|
||||||
|
@ -140,7 +140,7 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
return '<br>'.join(bills)
|
return '<br>'.join(bills)
|
||||||
bills_links.short_description = _("Bills")
|
bills_links.short_description = _("Bills")
|
||||||
bills_links.allow_tags = True
|
bills_links.allow_tags = True
|
||||||
|
|
||||||
def display_billed_until(self, order):
|
def display_billed_until(self, order):
|
||||||
billed_until = order.billed_until
|
billed_until = order.billed_until
|
||||||
red = False
|
red = False
|
||||||
|
@ -163,7 +163,7 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
display_billed_until.short_description = _("billed until")
|
display_billed_until.short_description = _("billed until")
|
||||||
display_billed_until.allow_tags = True
|
display_billed_until.allow_tags = True
|
||||||
display_billed_until.admin_order_field = 'billed_until'
|
display_billed_until.admin_order_field = 'billed_until'
|
||||||
|
|
||||||
def display_metric(self, order):
|
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
|
||||||
|
@ -174,7 +174,7 @@ class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
return ''
|
return ''
|
||||||
return metric.value
|
return metric.value
|
||||||
display_metric.short_description = _("Metric")
|
display_metric.short_description = _("Metric")
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Make value input widget bigger """
|
""" Make value input widget bigger """
|
||||||
if db_field.name == 'description':
|
if db_field.name == 'description':
|
||||||
|
|
|
@ -2,7 +2,7 @@ from functools import partial
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.admin import actions
|
from django.contrib.admin import actions
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.http import HttpResponseRedirect
|
from django.http import HttpResponseRedirect
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -48,20 +48,20 @@ class TransactionInline(admin.TabularInline):
|
||||||
'amount', 'currency'
|
'amount', 'currency'
|
||||||
)
|
)
|
||||||
readonly_fields = fields
|
readonly_fields = fields
|
||||||
|
|
||||||
transaction_link = admin_link('__str__', short_description=_("ID"))
|
transaction_link = admin_link('__str__', short_description=_("ID"))
|
||||||
bill_link = admin_link('bill')
|
bill_link = admin_link('bill')
|
||||||
source_link = admin_link('source')
|
source_link = admin_link('source')
|
||||||
display_state = admin_colored('state', colors=STATE_COLORS)
|
display_state = admin_colored('state', colors=STATE_COLORS)
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
'all': ('orchestra/css/hide-inline-id.css',)
|
'all': ('orchestra/css/hide-inline-id.css',)
|
||||||
}
|
}
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_queryset(self, *args, **kwargs):
|
def get_queryset(self, *args, **kwargs):
|
||||||
qs = super().get_queryset(*args, **kwargs)
|
qs = super().get_queryset(*args, **kwargs)
|
||||||
return qs.select_related('source', 'bill')
|
return qs.select_related('source', 'bill')
|
||||||
|
@ -116,28 +116,28 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
)
|
)
|
||||||
list_select_related = ('source', 'bill__account', 'process')
|
list_select_related = ('source', 'bill__account', 'process')
|
||||||
date_hierarchy = 'created_at'
|
date_hierarchy = 'created_at'
|
||||||
|
|
||||||
bill_link = admin_link('bill')
|
bill_link = admin_link('bill')
|
||||||
source_link = admin_link('source')
|
source_link = admin_link('source')
|
||||||
process_link = admin_link('process', short_description=_("proc"))
|
process_link = admin_link('process', short_description=_("proc"))
|
||||||
account_link = admin_link('bill__account')
|
account_link = admin_link('bill__account')
|
||||||
display_created_at = admin_date('created_at', short_description=_("Created"))
|
display_created_at = admin_date('created_at', short_description=_("Created"))
|
||||||
display_modified_at = admin_date('modified_at', short_description=_("Modified"))
|
display_modified_at = admin_date('modified_at', short_description=_("Modified"))
|
||||||
|
|
||||||
def has_delete_permission(self, *args, **kwargs):
|
def has_delete_permission(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_actions(self, request):
|
def get_actions(self, request):
|
||||||
actions = super().get_actions(request)
|
actions = super().get_actions(request)
|
||||||
if 'delete_selected' in actions:
|
if 'delete_selected' in actions:
|
||||||
del actions['delete_selected']
|
del actions['delete_selected']
|
||||||
return actions
|
return actions
|
||||||
|
|
||||||
def get_change_readonly_fields(self, request, obj):
|
def get_change_readonly_fields(self, request, obj):
|
||||||
if obj.state in (Transaction.WAITTING_PROCESSING, Transaction.WAITTING_EXECUTION):
|
if obj.state in (Transaction.WAITTING_PROCESSING, Transaction.WAITTING_EXECUTION):
|
||||||
return ()
|
return ()
|
||||||
return ('amount', 'currency')
|
return ('amount', 'currency')
|
||||||
|
|
||||||
def get_change_view_actions(self, obj=None):
|
def get_change_view_actions(self, obj=None):
|
||||||
actions = super(TransactionAdmin, self).get_change_view_actions()
|
actions = super(TransactionAdmin, self).get_change_view_actions()
|
||||||
exclude = []
|
exclude = []
|
||||||
|
@ -153,7 +153,7 @@ class TransactionAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
elif obj.state == Transaction.SECURED:
|
elif obj.state == Transaction.SECURED:
|
||||||
return []
|
return []
|
||||||
return [action for action in actions if action.__name__ not in exclude]
|
return [action for action in actions if action.__name__ not in exclude]
|
||||||
|
|
||||||
def display_state(self, obj):
|
def display_state(self, obj):
|
||||||
state = admin_colored('state', colors=STATE_COLORS)(obj)
|
state = admin_colored('state', colors=STATE_COLORS)(obj)
|
||||||
help_text = obj.get_state_help()
|
help_text = obj.get_state_help()
|
||||||
|
@ -178,16 +178,16 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
actions.mark_process_as_executed, actions.abort, actions.commit, actions.report
|
actions.mark_process_as_executed, actions.abort, actions.commit, actions.report
|
||||||
)
|
)
|
||||||
actions = change_view_actions + (actions.delete_selected,)
|
actions = change_view_actions + (actions.delete_selected,)
|
||||||
|
|
||||||
display_state = admin_colored('state', colors=PROCESS_STATE_COLORS)
|
display_state = admin_colored('state', colors=PROCESS_STATE_COLORS)
|
||||||
display_created_at = admin_date('created_at', short_description=_("Created"))
|
display_created_at = admin_date('created_at', short_description=_("Created"))
|
||||||
|
|
||||||
def file_url(self, process):
|
def file_url(self, process):
|
||||||
if process.file:
|
if process.file:
|
||||||
return '<a href="%s">%s</a>' % (process.file.url, process.file.name)
|
return '<a href="%s">%s</a>' % (process.file.url, process.file.name)
|
||||||
file_url.allow_tags = True
|
file_url.allow_tags = True
|
||||||
file_url.admin_order_field = 'file'
|
file_url.admin_order_field = 'file'
|
||||||
|
|
||||||
def display_transactions(self, process):
|
def display_transactions(self, process):
|
||||||
ids = []
|
ids = []
|
||||||
lines = []
|
lines = []
|
||||||
|
@ -208,10 +208,10 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
return '<a href="%s">%s</a>' % (url, transactions)
|
return '<a href="%s">%s</a>' % (url, transactions)
|
||||||
display_transactions.short_description = _("Transactions")
|
display_transactions.short_description = _("Transactions")
|
||||||
display_transactions.allow_tags = True
|
display_transactions.allow_tags = True
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_change_view_actions(self, obj=None):
|
def get_change_view_actions(self, obj=None):
|
||||||
actions = super().get_change_view_actions()
|
actions = super().get_change_view_actions()
|
||||||
exclude = []
|
exclude = []
|
||||||
|
@ -223,7 +223,7 @@ class TransactionProcessAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
elif obj.state == TransactionProcess.ABORTED:
|
elif obj.state == TransactionProcess.ABORTED:
|
||||||
exclude = ['mark_process_as_executed', 'abort', 'commit']
|
exclude = ['mark_process_as_executed', 'abort', 'commit']
|
||||||
return [action for action in actions if action.__name__ not in exclude]
|
return [action for action in actions if action.__name__ not in exclude]
|
||||||
|
|
||||||
def delete_view(self, request, object_id, extra_context=None):
|
def delete_view(self, request, object_id, extra_context=None):
|
||||||
queryset = self.model.objects.filter(id=object_id)
|
queryset = self.model.objects.filter(id=object_id)
|
||||||
related_transactions = helpers.pre_delete_processes(self, request, queryset)
|
related_transactions = helpers.pre_delete_processes(self, request, queryset)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class PlanAdmin(ExtendedModelAdmin):
|
||||||
}
|
}
|
||||||
change_readonly_fields = ('name',)
|
change_readonly_fields = ('name',)
|
||||||
inlines = [RateInline]
|
inlines = [RateInline]
|
||||||
|
|
||||||
def num_contracts(self, plan):
|
def num_contracts(self, plan):
|
||||||
num = plan.contracts__count
|
num = plan.contracts__count
|
||||||
url = reverse('admin:plans_contractedplan_changelist')
|
url = reverse('admin:plans_contractedplan_changelist')
|
||||||
|
@ -37,7 +37,7 @@ class PlanAdmin(ExtendedModelAdmin):
|
||||||
num_contracts.short_description = _("Contracts")
|
num_contracts.short_description = _("Contracts")
|
||||||
num_contracts.admin_order_field = 'contracts__count'
|
num_contracts.admin_order_field = 'contracts__count'
|
||||||
num_contracts.allow_tags = True
|
num_contracts.allow_tags = True
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super(PlanAdmin, self).get_queryset(request)
|
qs = super(PlanAdmin, self).get_queryset(request)
|
||||||
return qs.annotate(models.Count('contracts', distinct=True))
|
return qs.annotate(models.Count('contracts', distinct=True))
|
||||||
|
@ -49,7 +49,7 @@ class ContractedPlanAdmin(AccountAdminMixin, admin.ModelAdmin):
|
||||||
list_select_related = ('plan', 'account')
|
list_select_related = ('plan', 'account')
|
||||||
search_fields = ('account__username', 'plan__name', 'id')
|
search_fields = ('account__username', 'plan__name', 'id')
|
||||||
actions = (list_accounts,)
|
actions = (list_accounts,)
|
||||||
|
|
||||||
plan_link = admin_link('plan')
|
plan_link = admin_link('plan')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ungettext, ugettext_lazy as _
|
from django.utils.translation import ungettext, ugettext_lazy as _
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.contrib import admin, messages
|
||||||
from django.contrib.contenttypes.admin import GenericTabularInline
|
from django.contrib.contenttypes.admin import GenericTabularInline
|
||||||
from django.contrib.contenttypes.forms import BaseGenericInlineFormSet
|
from django.contrib.contenttypes.forms import BaseGenericInlineFormSet
|
||||||
from django.contrib.admin.utils import unquote
|
from django.contrib.admin.utils import unquote
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.templatetags.static import static
|
from django.templatetags.static import static
|
||||||
|
@ -58,7 +58,7 @@ class ResourceAdmin(ExtendedModelAdmin):
|
||||||
'name': ('verbose_name',)
|
'name': ('verbose_name',)
|
||||||
}
|
}
|
||||||
list_select_related = ('content_type', 'crontab',)
|
list_select_related = ('content_type', 'crontab',)
|
||||||
|
|
||||||
def change_view(self, request, object_id, form_url='', extra_context=None):
|
def change_view(self, request, object_id, form_url='', extra_context=None):
|
||||||
""" Remaind user when monitor routes are not configured """
|
""" Remaind user when monitor routes are not configured """
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
|
@ -78,7 +78,7 @@ class ResourceAdmin(ExtendedModelAdmin):
|
||||||
})
|
})
|
||||||
return super(ResourceAdmin, self).change_view(request, object_id, form_url=form_url,
|
return super(ResourceAdmin, self).change_view(request, object_id, form_url=form_url,
|
||||||
extra_context=extra_context)
|
extra_context=extra_context)
|
||||||
|
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
super(ResourceAdmin, self).save_model(request, obj, form, change)
|
super(ResourceAdmin, self).save_model(request, obj, form, change)
|
||||||
# best-effort
|
# best-effort
|
||||||
|
@ -93,7 +93,7 @@ class ResourceAdmin(ExtendedModelAdmin):
|
||||||
modeladmin.inlines = inlines
|
modeladmin.inlines = inlines
|
||||||
# reload Not always work
|
# reload Not always work
|
||||||
sys.touch_wsgi()
|
sys.touch_wsgi()
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" filter service content_types """
|
""" filter service content_types """
|
||||||
if db_field.name == 'content_type':
|
if db_field.name == 'content_type':
|
||||||
|
@ -127,10 +127,10 @@ class ResourceDataAdmin(ExtendedModelAdmin):
|
||||||
change_view_actions = actions
|
change_view_actions = actions
|
||||||
ordering = ('-updated_at',)
|
ordering = ('-updated_at',)
|
||||||
list_select_related = ('resource__content_type', 'content_type')
|
list_select_related = ('resource__content_type', 'content_type')
|
||||||
|
|
||||||
resource_link = admin_link('resource')
|
resource_link = admin_link('resource')
|
||||||
display_updated = admin_date('updated_at', short_description=_("Updated"))
|
display_updated = admin_date('updated_at', short_description=_("Updated"))
|
||||||
|
|
||||||
def get_urls(self):
|
def get_urls(self):
|
||||||
"""Returns the additional urls for the change view links"""
|
"""Returns the additional urls for the change view links"""
|
||||||
urls = super(ResourceDataAdmin, self).get_urls()
|
urls = super(ResourceDataAdmin, self).get_urls()
|
||||||
|
@ -150,7 +150,7 @@ class ResourceDataAdmin(ExtendedModelAdmin):
|
||||||
name='%s_%s_list_related' % (opts.app_label, opts.model_name)
|
name='%s_%s_list_related' % (opts.app_label, opts.model_name)
|
||||||
),
|
),
|
||||||
] + urls
|
] + urls
|
||||||
|
|
||||||
def display_used(self, rdata):
|
def display_used(self, rdata):
|
||||||
if rdata.used is None:
|
if rdata.used is None:
|
||||||
return ''
|
return ''
|
||||||
|
@ -159,15 +159,15 @@ class ResourceDataAdmin(ExtendedModelAdmin):
|
||||||
display_used.short_description = _("Used")
|
display_used.short_description = _("Used")
|
||||||
display_used.admin_order_field = 'used'
|
display_used.admin_order_field = 'used'
|
||||||
display_used.allow_tags = True
|
display_used.allow_tags = True
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def used_monitordata_view(self, request, object_id):
|
def used_monitordata_view(self, request, object_id):
|
||||||
url = reverse('admin:resources_monitordata_changelist')
|
url = reverse('admin:resources_monitordata_changelist')
|
||||||
url += '?resource_data=%s' % object_id
|
url += '?resource_data=%s' % object_id
|
||||||
return redirect(url)
|
return redirect(url)
|
||||||
|
|
||||||
def list_related_view(self, request, app_name, model_name, object_id):
|
def list_related_view(self, request, app_name, model_name, object_id):
|
||||||
resources = Resource.objects.select_related('content_type')
|
resources = Resource.objects.select_related('content_type')
|
||||||
resource_models = {r.content_type.model_class(): r.content_type_id for r in resources}
|
resource_models = {r.content_type.model_class(): r.content_type_id for r in resources}
|
||||||
|
@ -203,9 +203,9 @@ class MonitorDataAdmin(ExtendedModelAdmin):
|
||||||
list_select_related = ('content_type',)
|
list_select_related = ('content_type',)
|
||||||
search_fields = ('content_object_repr',)
|
search_fields = ('content_object_repr',)
|
||||||
date_hierarchy = 'created_at'
|
date_hierarchy = 'created_at'
|
||||||
|
|
||||||
display_created = admin_date('created_at', short_description=_("Created"))
|
display_created = admin_date('created_at', short_description=_("Created"))
|
||||||
|
|
||||||
def filter_used_monitordata(self, request, queryset):
|
def filter_used_monitordata(self, request, queryset):
|
||||||
query_string = parse_qs(request.META['QUERY_STRING'])
|
query_string = parse_qs(request.META['QUERY_STRING'])
|
||||||
resource_data = query_string.get('resource_data')
|
resource_data = query_string.get('resource_data')
|
||||||
|
@ -221,7 +221,7 @@ class MonitorDataAdmin(ExtendedModelAdmin):
|
||||||
ids += dataset.values_list('id', flat=True)
|
ids += dataset.values_list('id', flat=True)
|
||||||
return queryset.filter(id__in=ids)
|
return queryset.filter(id__in=ids)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
queryset = super(MonitorDataAdmin, self).get_queryset(request)
|
queryset = super(MonitorDataAdmin, self).get_queryset(request)
|
||||||
queryset = self.filter_used_monitordata(request, queryset)
|
queryset = self.filter_used_monitordata(request, queryset)
|
||||||
|
@ -239,13 +239,13 @@ def resource_inline_factory(resources):
|
||||||
class ResourceInlineFormSet(BaseGenericInlineFormSet):
|
class ResourceInlineFormSet(BaseGenericInlineFormSet):
|
||||||
def total_form_count(self, resources=resources):
|
def total_form_count(self, resources=resources):
|
||||||
return len(resources)
|
return len(resources)
|
||||||
|
|
||||||
@cached
|
@cached
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
""" Filter disabled resources """
|
""" Filter disabled resources """
|
||||||
queryset = super(ResourceInlineFormSet, self).get_queryset()
|
queryset = super(ResourceInlineFormSet, self).get_queryset()
|
||||||
return queryset.filter(resource__is_active=True).select_related('resource')
|
return queryset.filter(resource__is_active=True).select_related('resource')
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def forms(self, resources=resources):
|
def forms(self, resources=resources):
|
||||||
forms = []
|
forms = []
|
||||||
|
@ -276,7 +276,7 @@ def resource_inline_factory(resources):
|
||||||
for i, resource in enumerate(resources_copy, len(queryset)):
|
for i, resource in enumerate(resources_copy, len(queryset)):
|
||||||
forms.append(self._construct_form(i, resource=resource))
|
forms.append(self._construct_form(i, resource=resource))
|
||||||
return forms
|
return forms
|
||||||
|
|
||||||
class ResourceInline(GenericTabularInline):
|
class ResourceInline(GenericTabularInline):
|
||||||
model = ResourceData
|
model = ResourceData
|
||||||
verbose_name_plural = _("resources")
|
verbose_name_plural = _("resources")
|
||||||
|
@ -287,14 +287,14 @@ def resource_inline_factory(resources):
|
||||||
'verbose_name', 'display_used', 'display_updated', 'allocated', 'unit',
|
'verbose_name', 'display_used', 'display_updated', 'allocated', 'unit',
|
||||||
)
|
)
|
||||||
readonly_fields = ('display_used', 'display_updated',)
|
readonly_fields = ('display_used', 'display_updated',)
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
'all': ('orchestra/css/hide-inline-id.css',)
|
'all': ('orchestra/css/hide-inline-id.css',)
|
||||||
}
|
}
|
||||||
|
|
||||||
display_updated = admin_date('updated_at', default=_("Never"))
|
display_updated = admin_date('updated_at', default=_("Never"))
|
||||||
|
|
||||||
def get_fieldsets(self, request, obj=None):
|
def get_fieldsets(self, request, obj=None):
|
||||||
if obj:
|
if obj:
|
||||||
opts = self.parent_model._meta
|
opts = self.parent_model._meta
|
||||||
|
@ -303,7 +303,7 @@ def resource_inline_factory(resources):
|
||||||
link = '<a href="%s">%s</a>' % (url, _("List related"))
|
link = '<a href="%s">%s</a>' % (url, _("List related"))
|
||||||
self.verbose_name_plural = mark_safe(_("Resources") + ' ' + link)
|
self.verbose_name_plural = mark_safe(_("Resources") + ' ' + link)
|
||||||
return super(ResourceInline, self).get_fieldsets(request, obj)
|
return super(ResourceInline, self).get_fieldsets(request, obj)
|
||||||
|
|
||||||
def display_used(self, rdata):
|
def display_used(self, rdata):
|
||||||
update = ''
|
update = ''
|
||||||
history = ''
|
history = ''
|
||||||
|
@ -330,11 +330,11 @@ def resource_inline_factory(resources):
|
||||||
return _("No monitor")
|
return _("No monitor")
|
||||||
display_used.short_description = _("Used")
|
display_used.short_description = _("Used")
|
||||||
display_used.allow_tags = True
|
display_used.allow_tags = True
|
||||||
|
|
||||||
def has_add_permission(self, *args, **kwargs):
|
def has_add_permission(self, *args, **kwargs):
|
||||||
""" Hidde add another """
|
""" Hidde add another """
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return ResourceInline
|
return ResourceInline
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core import validators
|
from django.core import validators
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -25,7 +25,7 @@ class PHPListForm(SaaSPasswordForm):
|
||||||
help_text=_("Dedicated mailbox used for reciving bounces."),
|
help_text=_("Dedicated mailbox used for reciving bounces."),
|
||||||
widget=SpanWidget(display=settings.SAAS_PHPLIST_BOUNCES_MAILBOX_NAME.replace(
|
widget=SpanWidget(display=settings.SAAS_PHPLIST_BOUNCES_MAILBOX_NAME.replace(
|
||||||
'%(', '<').replace(')s', '>')))
|
'%(', '<').replace(')s', '>')))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(PHPListForm, self).__init__(*args, **kwargs)
|
super(PHPListForm, self).__init__(*args, **kwargs)
|
||||||
self.fields['name'].label = _("Site name")
|
self.fields['name'].label = _("Site name")
|
||||||
|
@ -76,14 +76,14 @@ class PHPListService(DBSoftwareService):
|
||||||
allow_custom_url = settings.SAAS_PHPLIST_ALLOW_CUSTOM_URL
|
allow_custom_url = settings.SAAS_PHPLIST_ALLOW_CUSTOM_URL
|
||||||
db_name = settings.SAAS_PHPLIST_DB_NAME
|
db_name = settings.SAAS_PHPLIST_DB_NAME
|
||||||
db_user = settings.SAAS_PHPLIST_DB_USER
|
db_user = settings.SAAS_PHPLIST_DB_USER
|
||||||
|
|
||||||
def get_mailbox_name(self):
|
def get_mailbox_name(self):
|
||||||
context = {
|
context = {
|
||||||
'name': self.instance.name,
|
'name': self.instance.name,
|
||||||
'site_name': self.instance.name,
|
'site_name': self.instance.name,
|
||||||
}
|
}
|
||||||
return settings.SAAS_PHPLIST_BOUNCES_MAILBOX_NAME % context
|
return settings.SAAS_PHPLIST_BOUNCES_MAILBOX_NAME % context
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(PHPListService, self).validate()
|
super(PHPListService, self).validate()
|
||||||
create = not self.instance.pk
|
create = not self.instance.pk
|
||||||
|
@ -97,7 +97,7 @@ class PHPListService(DBSoftwareService):
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'name': e.messages,
|
'name': e.messages,
|
||||||
})
|
})
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
super(PHPListService, self).save()
|
super(PHPListService, self).save()
|
||||||
account = self.get_account()
|
account = self.get_account()
|
||||||
|
@ -111,7 +111,7 @@ class PHPListService(DBSoftwareService):
|
||||||
'mailbox_id': mailbox.pk,
|
'mailbox_id': mailbox.pk,
|
||||||
'mailbox_name': mailbox_name,
|
'mailbox_name': mailbox_name,
|
||||||
})
|
})
|
||||||
|
|
||||||
def delete(self):
|
def delete(self):
|
||||||
super(PHPListService, self).save()
|
super(PHPListService, self).save()
|
||||||
account = self.get_account()
|
account = self.get_account()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from django.contrib.admin import helpers
|
from django.contrib.admin import helpers
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -42,7 +42,7 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
actions = (update_orders, clone, disable, enable)
|
actions = (update_orders, clone, disable, enable)
|
||||||
change_view_actions = actions + (view_help,)
|
change_view_actions = actions + (view_help,)
|
||||||
change_form_template = 'admin/services/service/change_form.html'
|
change_form_template = 'admin/services/service/change_form.html'
|
||||||
|
|
||||||
def get_urls(self):
|
def get_urls(self):
|
||||||
"""Returns the additional urls for the change view links"""
|
"""Returns the additional urls for the change view links"""
|
||||||
urls = super(ServiceAdmin, self).get_urls()
|
urls = super(ServiceAdmin, self).get_urls()
|
||||||
|
@ -54,7 +54,7 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
name='%s_%s_help' % (opts.app_label, opts.model_name)
|
name='%s_%s_help' % (opts.app_label, opts.model_name)
|
||||||
)
|
)
|
||||||
] + urls
|
] + urls
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
""" Improve performance of account field and filter by account """
|
""" Improve performance of account field and filter by account """
|
||||||
if db_field.name == 'content_type':
|
if db_field.name == 'content_type':
|
||||||
|
@ -64,7 +64,7 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
if db_field.name in ['match', 'metric', 'order_description']:
|
if db_field.name in ['match', 'metric', 'order_description']:
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'160'})
|
kwargs['widget'] = forms.TextInput(attrs={'size':'160'})
|
||||||
return super(ServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
return super(ServiceAdmin, self).formfield_for_dbfield(db_field, **kwargs)
|
||||||
|
|
||||||
def num_orders(self, service):
|
def num_orders(self, service):
|
||||||
num = service.orders__count
|
num = service.orders__count
|
||||||
url = reverse('admin:orders_order_changelist')
|
url = reverse('admin:orders_order_changelist')
|
||||||
|
@ -73,7 +73,7 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
num_orders.short_description = _("Orders")
|
num_orders.short_description = _("Orders")
|
||||||
num_orders.admin_order_field = 'orders__count'
|
num_orders.admin_order_field = 'orders__count'
|
||||||
num_orders.allow_tags = True
|
num_orders.allow_tags = True
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super(ServiceAdmin, self).get_queryset(request)
|
qs = super(ServiceAdmin, self).get_queryset(request)
|
||||||
# Count active orders
|
# Count active orders
|
||||||
|
@ -88,7 +88,7 @@ class ServiceAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
def help_view(self, request, *args):
|
def help_view(self, request, *args):
|
||||||
opts = self.model._meta
|
opts = self.model._meta
|
||||||
context = {
|
context = {
|
||||||
|
|
|
@ -8,7 +8,7 @@ from functools import partial
|
||||||
import paramiko
|
import paramiko
|
||||||
from django.conf import settings as djsettings
|
from django.conf import settings as djsettings
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from selenium.webdriver.support.select import Select
|
from selenium.webdriver.support.select import Select
|
||||||
|
|
||||||
from orchestra.admin.utils import change_url
|
from orchestra.admin.utils import change_url
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext, ugettext_lazy as _
|
from django.utils.translation import ugettext, ugettext_lazy as _
|
||||||
|
|
||||||
|
@ -21,16 +21,16 @@ from .types import AppType
|
||||||
class WebAppOptionInline(admin.TabularInline):
|
class WebAppOptionInline(admin.TabularInline):
|
||||||
model = WebAppOption
|
model = WebAppOption
|
||||||
extra = 1
|
extra = 1
|
||||||
|
|
||||||
OPTIONS_HELP_TEXT = {
|
OPTIONS_HELP_TEXT = {
|
||||||
op.name: force_text(op.help_text) for op in AppOption.get_plugins()
|
op.name: force_text(op.help_text) for op in AppOption.get_plugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
css = {
|
css = {
|
||||||
'all': ('orchestra/css/hide-inline-id.css',)
|
'all': ('orchestra/css/hide-inline-id.css',)
|
||||||
}
|
}
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
if db_field.name == 'value':
|
if db_field.name == 'value':
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
|
kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
|
||||||
|
@ -63,9 +63,9 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
|
||||||
plugin_field = 'type'
|
plugin_field = 'type'
|
||||||
plugin_title = _("Web application type")
|
plugin_title = _("Web application type")
|
||||||
actions = (list_accounts,)
|
actions = (list_accounts,)
|
||||||
|
|
||||||
display_type = display_plugin_field('type')
|
display_type = display_plugin_field('type')
|
||||||
|
|
||||||
def display_websites(self, webapp):
|
def display_websites(self, webapp):
|
||||||
websites = []
|
websites = []
|
||||||
for content in webapp.content_set.all():
|
for content in webapp.content_set.all():
|
||||||
|
@ -83,7 +83,7 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
|
||||||
return '<br>'.join(websites)
|
return '<br>'.join(websites)
|
||||||
display_websites.short_description = _("web sites")
|
display_websites.short_description = _("web sites")
|
||||||
display_websites.allow_tags = True
|
display_websites.allow_tags = True
|
||||||
|
|
||||||
def display_detail(self, webapp):
|
def display_detail(self, webapp):
|
||||||
try:
|
try:
|
||||||
return webapp.type_instance.get_detail()
|
return webapp.type_instance.get_detail()
|
||||||
|
@ -91,11 +91,11 @@ class WebAppAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin)
|
||||||
return "<span style='color:red;'>Not available</span>"
|
return "<span style='color:red;'>Not available</span>"
|
||||||
display_detail.short_description = _("detail")
|
display_detail.short_description = _("detail")
|
||||||
display_detail.allow_tags = True
|
display_detail.allow_tags = True
|
||||||
|
|
||||||
# def get_form(self, request, obj=None, **kwargs):
|
# def get_form(self, request, obj=None, **kwargs):
|
||||||
# form = super(WebAppAdmin, self).get_form(request, obj, **kwargs)
|
# form = super(WebAppAdmin, self).get_form(request, obj, **kwargs)
|
||||||
# if obj:
|
# if obj:
|
||||||
#
|
#
|
||||||
|
|
||||||
# def formfield_for_dbfield(self, db_field, **kwargs):
|
# def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
# """ Make value input widget bigger """
|
# """ Make value input widget bigger """
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
@ -20,7 +20,7 @@ class CMSAppForm(PHPAppForm):
|
||||||
password = forms.CharField(label=_("Password"),
|
password = forms.CharField(label=_("Password"),
|
||||||
help_text=_("Initial database and WordPress admin password.<br>"
|
help_text=_("Initial database and WordPress admin password.<br>"
|
||||||
"Subsequent changes to the admin password will not be reflected."))
|
"Subsequent changes to the admin password will not be reflected."))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(CMSAppForm, self).__init__(*args, **kwargs)
|
super(CMSAppForm, self).__init__(*args, **kwargs)
|
||||||
if self.instance:
|
if self.instance:
|
||||||
|
@ -55,20 +55,20 @@ class CMSApp(PHPApp):
|
||||||
db_type = Database.MYSQL
|
db_type = Database.MYSQL
|
||||||
abstract = True
|
abstract = True
|
||||||
db_prefix = 'cms_'
|
db_prefix = 'cms_'
|
||||||
|
|
||||||
def get_db_name(self):
|
def get_db_name(self):
|
||||||
db_name = '%s%s_%s' % (self.db_prefix, self.instance.name, self.instance.account)
|
db_name = '%s%s_%s' % (self.db_prefix, self.instance.name, self.instance.account)
|
||||||
# Limit for mysql database names
|
# Limit for mysql database names
|
||||||
return db_name[:65]
|
return db_name[:65]
|
||||||
|
|
||||||
def get_db_user(self):
|
def get_db_user(self):
|
||||||
db_name = self.get_db_name()
|
db_name = self.get_db_name()
|
||||||
# Limit for mysql user names
|
# Limit for mysql user names
|
||||||
return db_name[:16]
|
return db_name[:16]
|
||||||
|
|
||||||
def get_password(self):
|
def get_password(self):
|
||||||
return random_ascii(10)
|
return random_ascii(10)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(CMSApp, self).validate()
|
super(CMSApp, self).validate()
|
||||||
create = not self.instance.pk
|
create = not self.instance.pk
|
||||||
|
@ -83,7 +83,7 @@ class CMSApp(PHPApp):
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
'name': e.messages,
|
'name': e.messages,
|
||||||
})
|
})
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
db_name = self.get_db_name()
|
db_name = self.get_db_name()
|
||||||
db_user = self.get_db_user()
|
db_user = self.get_db_user()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.urlresolvers import resolve
|
from django.urls import resolve
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.utils.encoding import force_text
|
from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
@ -24,11 +24,11 @@ class WebsiteDirectiveInline(admin.TabularInline):
|
||||||
model = WebsiteDirective
|
model = WebsiteDirective
|
||||||
formset = WebsiteDirectiveInlineFormSet
|
formset = WebsiteDirectiveInlineFormSet
|
||||||
extra = 1
|
extra = 1
|
||||||
|
|
||||||
DIRECTIVES_HELP_TEXT = {
|
DIRECTIVES_HELP_TEXT = {
|
||||||
op.name: force_text(op.help_text) for op in SiteDirective.get_plugins()
|
op.name: force_text(op.help_text) for op in SiteDirective.get_plugins()
|
||||||
}
|
}
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
if db_field.name == 'value':
|
if db_field.name == 'value':
|
||||||
kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
|
kwargs['widget'] = forms.TextInput(attrs={'size':'100'})
|
||||||
|
@ -45,10 +45,10 @@ class ContentInline(AccountAdminMixin, admin.TabularInline):
|
||||||
fields = ('webapp', 'webapp_link', 'webapp_type', 'path')
|
fields = ('webapp', 'webapp_link', 'webapp_type', 'path')
|
||||||
readonly_fields = ('webapp_link', 'webapp_type')
|
readonly_fields = ('webapp_link', 'webapp_type')
|
||||||
filter_by_account_fields = ['webapp']
|
filter_by_account_fields = ['webapp']
|
||||||
|
|
||||||
webapp_link = admin_link('webapp', popup=True)
|
webapp_link = admin_link('webapp', popup=True)
|
||||||
webapp_link.short_description = _("Web App")
|
webapp_link.short_description = _("Web App")
|
||||||
|
|
||||||
def webapp_type(self, content):
|
def webapp_type(self, content):
|
||||||
if not content.pk:
|
if not content.pk:
|
||||||
return ''
|
return ''
|
||||||
|
@ -77,7 +77,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
list_prefetch_related = ('domains', 'content_set__webapp')
|
list_prefetch_related = ('domains', 'content_set__webapp')
|
||||||
search_fields = ('name', 'account__username', 'domains__name', 'content__webapp__name')
|
search_fields = ('name', 'account__username', 'domains__name', 'content__webapp__name')
|
||||||
actions = (disable, enable, list_accounts)
|
actions = (disable, enable, list_accounts)
|
||||||
|
|
||||||
def display_domains(self, website):
|
def display_domains(self, website):
|
||||||
domains = []
|
domains = []
|
||||||
for domain in website.domains.all():
|
for domain in website.domains.all():
|
||||||
|
@ -87,7 +87,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
display_domains.short_description = _("domains")
|
display_domains.short_description = _("domains")
|
||||||
display_domains.allow_tags = True
|
display_domains.allow_tags = True
|
||||||
display_domains.admin_order_field = 'domains'
|
display_domains.admin_order_field = 'domains'
|
||||||
|
|
||||||
def display_webapps(self, website):
|
def display_webapps(self, website):
|
||||||
webapps = []
|
webapps = []
|
||||||
for content in website.content_set.all():
|
for content in website.content_set.all():
|
||||||
|
@ -104,7 +104,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
return '<br>'.join(webapps)
|
return '<br>'.join(webapps)
|
||||||
display_webapps.allow_tags = True
|
display_webapps.allow_tags = True
|
||||||
display_webapps.short_description = _("Web apps")
|
display_webapps.short_description = _("Web apps")
|
||||||
|
|
||||||
def formfield_for_dbfield(self, db_field, **kwargs):
|
def formfield_for_dbfield(self, db_field, **kwargs):
|
||||||
"""
|
"""
|
||||||
Exclude domains with exhausted ports
|
Exclude domains with exhausted ports
|
||||||
|
@ -124,7 +124,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
qset = Q(qset & ~Q(websites__pk=object_id))
|
qset = Q(qset & ~Q(websites__pk=object_id))
|
||||||
formfield.queryset = formfield.queryset.exclude(qset)
|
formfield.queryset = formfield.queryset.exclude(qset)
|
||||||
return formfield
|
return formfield
|
||||||
|
|
||||||
def _create_formsets(self, request, obj, change):
|
def _create_formsets(self, request, obj, change):
|
||||||
""" bind contents formset to directive formset for unique location cross-validation """
|
""" bind contents formset to directive formset for unique location cross-validation """
|
||||||
formsets, inline_instances = super(WebsiteAdmin, self)._create_formsets(request, obj, change)
|
formsets, inline_instances = super(WebsiteAdmin, self)._create_formsets(request, obj, change)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from django.core import exceptions
|
from django.core import exceptions
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields.files import FileField, FieldFile
|
from django.db.models.fields.files import FileField, FieldFile
|
||||||
from django.utils.text import capfirst
|
from django.utils.text import capfirst
|
||||||
|
@ -21,34 +21,34 @@ class MultiSelectField(models.CharField):
|
||||||
defaults['initial'] = self.get_default()
|
defaults['initial'] = self.get_default()
|
||||||
defaults.update(kwargs)
|
defaults.update(kwargs)
|
||||||
return MultiSelectFormField(**defaults)
|
return MultiSelectFormField(**defaults)
|
||||||
|
|
||||||
def get_db_prep_value(self, value, connection=None, prepared=False):
|
def get_db_prep_value(self, value, connection=None, prepared=False):
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
else:
|
else:
|
||||||
return ','.join(value)
|
return ','.join(value)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if value:
|
if value:
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
return value.split(',')
|
return value.split(',')
|
||||||
return value
|
return value
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def from_db_value(self, value, expression, connection, context):
|
def from_db_value(self, value, expression, connection, context):
|
||||||
if value:
|
if value:
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
return value.split(',')
|
return value.split(',')
|
||||||
return value
|
return value
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def contribute_to_class(self, cls, name):
|
def contribute_to_class(self, cls, name):
|
||||||
super(MultiSelectField, self).contribute_to_class(cls, name)
|
super(MultiSelectField, self).contribute_to_class(cls, name)
|
||||||
if self.choices:
|
if self.choices:
|
||||||
def func(self, field=name, choices=dict(self.choices)):
|
def func(self, field=name, choices=dict(self.choices)):
|
||||||
return ','.join([choices.get(value, value) for value in getattr(self, field)])
|
return ','.join([choices.get(value, value) for value in getattr(self, field)])
|
||||||
setattr(cls, 'get_%s_display' % self.name, func)
|
setattr(cls, 'get_%s_display' % self.name, func)
|
||||||
|
|
||||||
def validate(self, value, model_instance):
|
def validate(self, value, model_instance):
|
||||||
if self.choices:
|
if self.choices:
|
||||||
arr_choices = self.get_choices_selected(self.get_choices())
|
arr_choices = self.get_choices_selected(self.get_choices())
|
||||||
|
@ -56,7 +56,7 @@ class MultiSelectField(models.CharField):
|
||||||
if (opt_select not in arr_choices):
|
if (opt_select not in arr_choices):
|
||||||
msg = self.error_messages['invalid_choice'] % {'value': opt_select}
|
msg = self.error_messages['invalid_choice'] % {'value': opt_select}
|
||||||
raise exceptions.ValidationError(msg)
|
raise exceptions.ValidationError(msg)
|
||||||
|
|
||||||
def get_choices_selected(self, arr_choices=''):
|
def get_choices_selected(self, arr_choices=''):
|
||||||
if not arr_choices:
|
if not arr_choices:
|
||||||
return False
|
return False
|
||||||
|
@ -79,11 +79,11 @@ class PrivateFieldFile(FieldFile):
|
||||||
filename = os.path.basename(self.path)
|
filename = os.path.basename(self.path)
|
||||||
args = [app_label, model_name, field_name, pk, filename]
|
args = [app_label, model_name, field_name, pk, filename]
|
||||||
return reverse('private-media', args=args)
|
return reverse('private-media', args=args)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def condition(self):
|
def condition(self):
|
||||||
return self.field.condition
|
return self.field.condition
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def attachment(self):
|
def attachment(self):
|
||||||
return self.field.attachment
|
return self.field.attachment
|
||||||
|
@ -91,7 +91,7 @@ class PrivateFieldFile(FieldFile):
|
||||||
|
|
||||||
class PrivateFileField(FileField):
|
class PrivateFileField(FileField):
|
||||||
attr_class = PrivateFieldFile
|
attr_class = PrivateFieldFile
|
||||||
|
|
||||||
def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, attachment=True,
|
def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, attachment=True,
|
||||||
condition=lambda request, instance: request.user.is_superuser, **kwargs):
|
condition=lambda request, instance: request.user.is_superuser, **kwargs):
|
||||||
super(PrivateFileField, self).__init__(verbose_name, name, upload_to, storage, **kwargs)
|
super(PrivateFileField, self).__init__(verbose_name, name, upload_to, storage, **kwargs)
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
from django.core.urlresolvers import resolve
|
from django.urls import resolve
|
||||||
from rest_framework.permissions import DjangoModelPermissions
|
from rest_framework.permissions import DjangoModelPermissions
|
||||||
|
|
||||||
|
|
||||||
class OrchestraPermissionBackend(DjangoModelPermissions):
|
class OrchestraPermissionBackend(DjangoModelPermissions):
|
||||||
""" Permissions according to each user """
|
""" Permissions according to each user """
|
||||||
|
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
queryset = getattr(view, 'queryset', None)
|
queryset = getattr(view, 'queryset', None)
|
||||||
if queryset is None:
|
if queryset is None:
|
||||||
name = resolve(request.path).url_name
|
name = resolve(request.path).url_name
|
||||||
return name == 'api-root'
|
return name == 'api-root'
|
||||||
|
|
||||||
model_cls = queryset.model
|
model_cls = queryset.model
|
||||||
perms = self.get_required_permissions(request.method, model_cls)
|
perms = self.get_required_permissions(request.method, model_cls)
|
||||||
if (request.user and
|
if (request.user and
|
||||||
|
@ -18,7 +18,7 @@ class OrchestraPermissionBackend(DjangoModelPermissions):
|
||||||
request.user.has_perms(perms, model_cls)):
|
request.user.has_perms(perms, model_cls)):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def has_object_permission(self, request, view, obj):
|
def has_object_permission(self, request, view, obj):
|
||||||
perms = self.get_required_permissions(request.method, type(obj))
|
perms = self.get_required_permissions(request.method, type(obj))
|
||||||
if (request.user and
|
if (request.user and
|
||||||
|
|
|
@ -4,7 +4,7 @@ import re
|
||||||
|
|
||||||
from django import template
|
from django import template
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.urlresolvers import reverse, NoReverseMatch
|
from django.urls import reverse, NoReverseMatch
|
||||||
from django.forms import CheckboxInput
|
from django.forms import CheckboxInput
|
||||||
from django.template.base import Node
|
from django.template.base import Node
|
||||||
from django.template.defaultfilters import date
|
from django.template.defaultfilters import date
|
||||||
|
@ -54,7 +54,7 @@ def rest_to_admin_url(context):
|
||||||
class OneLinerNode(Node):
|
class OneLinerNode(Node):
|
||||||
def __init__(self, nodelist):
|
def __init__(self, nodelist):
|
||||||
self.nodelist = nodelist
|
self.nodelist = nodelist
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
line = self.nodelist.render(context).replace('\n', ' ')
|
line = self.nodelist.render(context).replace('\n', ' ')
|
||||||
return re.sub(r'\s\s+', '', line)
|
return re.sub(r'\s\s+', '', line)
|
||||||
|
|
|
@ -5,7 +5,7 @@ from functools import wraps
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import BACKEND_SESSION_KEY, SESSION_KEY
|
from django.contrib.auth import BACKEND_SESSION_KEY, SESSION_KEY
|
||||||
from django.contrib.sessions.backends.db import SessionStore
|
from django.contrib.sessions.backends.db import SessionStore
|
||||||
from django.core.urlresolvers import reverse
|
from django.urls import reverse
|
||||||
from django.test import LiveServerTestCase, TestCase
|
from django.test import LiveServerTestCase, TestCase
|
||||||
from selenium.webdriver.firefox.webdriver import WebDriver
|
from selenium.webdriver.firefox.webdriver import WebDriver
|
||||||
from xvfbwrapper import Xvfb
|
from xvfbwrapper import Xvfb
|
||||||
|
|
Loading…
Reference in New Issue