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