Example #1
0
 def display_summary(self, ticket):
     context = {
         'creator': admin_link('creator')(self, ticket) if ticket.creator else ticket.creator_name,
         'created': admin_date('created_at')(ticket),
         'updated': '',
     }
     msg = ticket.messages.last()
     if msg:
         context.update({
             'updated': admin_date('created_at')(msg),
             'updater': admin_link('author')(self, msg) if msg.author else msg.author_name,
         })
         context['updated'] = '. Updated by %(updater)s about %(updated)s' % context
     return '<h4>Added by %(creator)s about %(created)s%(updated)s</h4>' % context
Example #2
0
 def display_summary(self, ticket):
     context = {
         'creator': admin_link('creator')(self, ticket) if ticket.creator else ticket.creator_name,
         'created': admin_date('created_on')(ticket),
         'updated': '',
     }
     msg = ticket.messages.last()
     if msg:
         context.update({
             'updated': admin_date('created_on')(msg),
             'updater': admin_link('author')(self, msg) if msg.author else msg.author_name,
         })
         context['updated'] = '. Updated by %(updater)s about %(updated)s' % context
     return '<h4>Added by %(creator)s about %(created)s%(updated)s</h4>' % context
Example #3
0
 def instance_link(self, operation):
     link = admin_link("instance")(self, operation)
     if link == "---":
         return _("Deleted {0}").format(
             operation.instance_repr or "-".join((escape(operation.content_type), escape(operation.object_id)))
         )
     return link
Example #4
0
class BillLineInline(admin.TabularInline):
    model = BillLine
    fields = (
        'description', 'order_link', 'start_on', 'end_on', 'rate', 'quantity', 'tax',
        'subtotal', 'display_total',
    )
    readonly_fields = ('display_total', 'order_link')

    order_link = admin_link('order', display='pk')

    @mark_safe
    def display_total(self, line):
        if line.pk:
            total = line.compute_total()
            sublines = line.sublines.all()
            url = change_url(line)
            if sublines:
                content = '\n'.join(['%s: %s' % (sub.description, sub.total) for sub in sublines])
                img = static('admin/img/icon-alert.svg')
                return '<a href="%s" title="%s">%s <img src="%s"></img></a>' % (url, content, total, img)
            return '<a href="%s">%s</a>' % (url, total)
    display_total.short_description = _("Total")

    def formfield_for_dbfield(self, db_field, **kwargs):
        """ Make value input widget bigger """
        if db_field.name == 'description':
            kwargs['widget'] = forms.TextInput(attrs={'size':'50'})
        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')
Example #5
0
 def instance_link(self, operation):
     try:
         return admin_link('instance')(self, operation)
     except:
         return _("deleted {0} {1}").format(
             escape(operation.content_type), escape(operation.object_id)
         )
Example #6
0
class ContractedPlanAdmin(AccountAdminMixin, admin.ModelAdmin):
    list_display = ('id', 'plan_link', 'account_link')
    list_filter = ('plan__name',)
    list_select_related = ('plan', 'account')
    search_fields = ('account__username', 'plan__name', 'id')
    actions = (list_accounts,)

    plan_link = admin_link('plan')
Example #7
0
 def __init__(self, *args, **kwargs):
     super(SelectSourceForm, self).__init__(*args, **kwargs)
     bill = kwargs.get('instance')
     if bill:
         total = bill.compute_total()
         sources = bill.account.paymentsources.filter(is_active=True)
         recharge = bool(total < 0)
         choices = [(None, '-----------')]
         for source in sources:
             if not recharge or source.method_class().allow_recharge:
                 choices.append((source.pk, str(source)))
         self.fields['source'].choices = choices
         self.fields['source'].initial = choices[-1][0]
         self.fields['show_total'].widget.display = total
         self.fields['bill_link'].widget.display = admin_link('__str__')(bill)
         self.fields['display_type'].widget.display = bill.get_type_display()
         self.fields['account_link'].widget.display = admin_link('account')(bill)
Example #8
0
 def __init__(self, *args, **kwargs):
     super(PluginForm, self).__init__(*args, **kwargs)
     if self.plugin_field in self.fields:
         # Provide a link to the related DB object change view
         value = self.plugin.related_instance.pk
         link = admin_link()(self.plugin.related_instance)
         display = '%s <a href=".">change</a>' % link
         self.fields[self.plugin_field].widget = SpanWidget(original=value, display=display)
         help_text = self.fields[self.plugin_field].help_text
Example #9
0
 def __init__(self, *args, **kwargs):
     super(PluginForm, self).__init__(*args, **kwargs)
     if self.plugin_field in self.fields:
         # Provide a link to the related DB object change view
         value = self.plugin.related_instance.pk
         link = admin_link()(self.plugin.related_instance)
         display = '%s <a href=".">change</a>' % link
         self.fields[self.plugin_field].widget = SpanWidget(original=value,
                                                            display=display)
         help_text = self.fields[self.plugin_field].help_text
Example #10
0
class SMTPLogAdmin(admin.ModelAdmin):
    list_display = ('id', 'message_link', 'colored_result', 'date_delta',
                    'log_message')
    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')
Example #11
0
 def display_forward(self, address):
     forward_mailboxes = {m.name: m for m in address.get_forward_mailboxes()}
     values = []
     for forward in address.forward.split():
         mbox = forward_mailboxes.get(forward)
         if mbox:
             values.append(admin_link()(mbox))
         else:
             values.append(forward)
     return '<br>'.join(values)
Example #12
0
 def __init__(self, *args, **kwargs):
     super(SelectSourceForm, self).__init__(*args, **kwargs)
     bill = kwargs.get('instance')
     if bill:
         total = bill.compute_total()
         sources = bill.account.paymentsources.filter(is_active=True)
         recharge = bool(total < 0)
         choices = [(None, '-----------')]
         for source in sources:
             if not recharge or source.method_class().allow_recharge:
                 choices.append((source.pk, str(source)))
         self.fields['source'].choices = choices
         self.fields['source'].initial = choices[-1][0]
         self.fields['show_total'].widget.display = total
         self.fields['bill_link'].widget.display = admin_link('__str__')(
             bill)
         self.fields['display_type'].widget.display = bill.get_type_display(
         )
         self.fields['account_link'].widget.display = admin_link('account')(
             bill)
Example #13
0
 def content_html(self, msg):
     context = {
         'number': msg.number,
         'time': admin_date('created_on')(msg),
         'author': admin_link('author')(msg) if msg.author else msg.author_name,
     }
     summary = _("#%(number)i Updated by %(author)s about %(time)s") % context
     header = '<strong style="color:#666;">%s</strong><hr />' % summary
     content = markdown(msg.content)
     content = content.replace('>\n', '>')
     content = '<div style="padding-left:20px;">%s</div>' % content
     return header + content
Example #14
0
 def display_forward(self, address):
     forward_mailboxes = {
         m.name: m
         for m in address.get_forward_mailboxes()
     }
     values = []
     for forward in address.forward.split():
         mbox = forward_mailboxes.get(forward)
         if mbox:
             values.append(admin_link()(mbox))
         else:
             values.append(forward)
     return '<br>'.join(values)
Example #15
0
class TransactionInline(admin.TabularInline):
    model = Transaction
    can_delete = False
    extra = 0
    fields = ('transaction_link', 'bill_link', 'source_link', 'display_state',
              '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')
Example #16
0
class AmendInline(BillAdminMixin, admin.TabularInline):
    model = Bill
    fields = ('self_link', 'type', 'display_total_with_subtotals',
              'display_payment_state', 'is_open', 'is_sent')
    readonly_fields = fields
    verbose_name_plural = _("Amends")
    can_delete = False
    extra = 0

    self_link = admin_link('__str__')

    def has_add_permission(self, *args, **kwargs):
        return False
Example #17
0
    def content_html(self, msg):
        context = {
            'number': msg.number,
            'time': admin_date('created_at')(msg),
            'author': admin_link('author')(msg) if msg.author else msg.author_name,
        }
        summary = _("#%(number)i Updated by %(author)s about %(time)s") % context
        header = '<strong style="color:#666;">%s</strong><hr />' % summary

        content = markdown(msg.content)
        content = content.replace('>\n', '>')
        content = '<div style="padding-left:20px;">%s</div>' % content

        return header + content
Example #18
0
class DomainInline(admin.TabularInline):
    model = Domain
    fields = ('domain_link', 'display_records', 'account_link')
    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)
        return qs.select_related('account').prefetch_related('records')
Example #19
0
 def display_websites(self, webapp):
     websites = []
     for content in webapp.content_set.all():
         site_url = content.get_absolute_url()
         site_link = get_on_site_link(site_url)
         website = content.website
         name = "%s on %s %s" % (website.name, content.path, site_link)
         link = admin_link(display=name)(website)
         websites.append(link)
     if not websites:
         add_url = reverse('admin:websites_website_add')
         add_url += '?account=%s' % webapp.account_id
         plus = '<strong style="color:green; font-size:12px">+</strong>'
         websites.append('<a href="%s">%s%s</a>' % (add_url, plus, ugettext("Add website")))
     return '<br>'.join(websites)
Example #20
0
class TicketInline(admin.TabularInline):
    fields = (
        'ticket_id', 'subject', 'creator_link', 'owner_link', 'colored_state',
        'colored_priority', 'created', 'updated'
    )
    readonly_fields = (
        'ticket_id', 'subject', 'creator_link', 'owner_link', 'colored_state',
        'colored_priority', 'created', 'updated'
    )
    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)

    @mark_safe
    def ticket_id(self, instance):
        return '<b>%s</b>' % admin_link()(instance)
    ticket_id.short_description = '#'
Example #21
0
class ContentInline(AccountAdminMixin, admin.TabularInline):
    model = Content
    extra = 1
    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 ''
        return content.webapp.get_type_display()

    webapp_type.short_description = _("Web App type")
Example #22
0
def set_soa(modeladmin, request, queryset):
    if queryset.filter(top__isnull=False).exists():
        msg = _("Set SOA on subdomains is not possible.")
        modeladmin.message_user(request, msg, messages.ERROR)
        return
    form = SOAForm()
    if request.POST.get('post') == 'generic_confirmation':
        form = SOAForm(request.POST)
        if form.is_valid():
            updates = {
                name: value
                for name, value in form.cleaned_data.items() if value
            }
            change_message = _("SOA set %s") % str(updates)[1:-1]
            for domain in queryset:
                for name, value in updates.items():
                    if name.startswith('clear_'):
                        name = name.replace('clear_', '')
                        value = ''
                    setattr(domain, name, value)
                modeladmin.log_change(request, domain, change_message)
                domain.save()
            num = len(queryset)
            msg = ungettext(
                _("SOA record for one domain has been updated."),
                _("SOA record for %s domains has been updated.") % num, num)
            modeladmin.message_user(request, msg)
            return
    opts = modeladmin.model._meta
    context = {
        'title': _("Set SOA for selected domains"),
        'content_message': '',
        'action_name': _("Set SOA"),
        'action_value': 'set_soa',
        'display_objects':
        [admin_link('__str__')(domain) for domain in queryset],
        'queryset': queryset,
        'opts': opts,
        'app_label': opts.app_label,
        'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
        'form': form,
        'obj': get_object_from_url(modeladmin, request),
    }
    return render(request, 'admin/orchestra/generic_confirmation.html',
                  context)
Example #23
0
def set_soa(modeladmin, request, queryset):
    if queryset.filter(top__isnull=False).exists():
        msg = _("Set SOA on subdomains is not possible.")
        modeladmin.message_user(request, msg, messages.ERROR)
        return
    form = SOAForm()
    if request.POST.get('post') == 'generic_confirmation':
        form = SOAForm(request.POST)
        if form.is_valid():
            updates = {name: value for name, value in form.cleaned_data.items() if value}
            change_message = _("SOA set %s") % str(updates)[1:-1]
            for domain in queryset:
                for name, value in updates.items():
                    if name.startswith('clear_'):
                        name = name.replace('clear_', '')
                        value = ''
                    setattr(domain, name, value)
                modeladmin.log_change(request, domain, change_message)
                domain.save()
            num = len(queryset)
            msg = ungettext(
                _("SOA record for one domain has been updated."),
                _("SOA record for %s domains has been updated.") % num,
                num
            )
            modeladmin.message_user(request, msg)
            return
    opts = modeladmin.model._meta
    context = {
        'title': _("Set SOA for selected domains"),
        'content_message': '',
        'action_name': _("Set SOA"),
        'action_value': 'set_soa',
        'display_objects': [admin_link('__str__')(domain) for domain in queryset],
        'queryset': queryset,
        'opts': opts,
        'app_label': opts.app_label,
        'action_checkbox_name': helpers.ACTION_CHECKBOX_NAME,
        'form': form,
        'obj': get_object_from_url(modeladmin, request),
    }
    return render(request, 'admin/orchestra/generic_confirmation.html', context)
Example #24
0
class BackendLogAdmin(ChangeViewActionsMixin, admin.ModelAdmin):
    list_display = (
        'id',
        'backend',
        'server_link',
        'display_state',
        'exit_code',
        'display_created',
        'execution_time',
    )
    list_display_links = ('id', 'backend')
    list_filter = ('state', 'server', 'backend', 'operations__action')
    search_fields = ('script', )
    date_hierarchy = 'created_at'
    inlines = (BackendOperationInline, )
    fields = ('backend', 'server_link', 'state', 'display_script',
              'mono_stdout', 'mono_stderr', 'mono_traceback', 'exit_code',
              'task_id', 'display_created', 'execution_time')
    readonly_fields = fields
    actions = (retry_backend, )
    change_view_actions = actions

    server_link = admin_link('server')
    display_created = admin_date('created_at', short_description=_("Created"))
    display_state = admin_colored('state', colors=STATE_COLORS)
    display_script = display_code('script')
    mono_stdout = display_mono('stdout')
    mono_stderr = display_mono('stderr')
    mono_traceback = display_mono('traceback')

    class Media:
        css = {'all': ('orchestra/css/pygments/github.css', )}

    def get_queryset(self, request):
        """ Order by structured name and imporve performance """
        qs = super(BackendLogAdmin, self).get_queryset(request)
        return qs.select_related('server').defer('script', 'stdout')

    def has_add_permission(self, *args, **kwargs):
        return False
Example #25
0
 def instance_link(self, operation):
     link = admin_link('instance')(self, operation)
     if link == '---':
         return _("Deleted {0}").format(operation.instance_repr or '-'.join(
             (escape(operation.content_type), escape(operation.object_id))))
     return link
Example #26
0
class ResourceDataAdmin(ExtendedModelAdmin):
    list_display = ('id', 'resource_link', content_object_link, 'allocated',
                    'display_used', 'display_updated')
    list_filter = ('resource', )
    fields = (
        'resource_link',
        'content_type',
        content_object_link,
        'display_updated',
        'display_used',
        'allocated',
    )
    search_fields = ('content_object_repr', )
    readonly_fields = fields
    actions = (run_monitor, show_history)
    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()
        admin_site = self.admin_site
        opts = self.model._meta
        return [
            url('^(\d+)/used-monitordata/$',
                admin_site.admin_view(self.used_monitordata_view),
                name='%s_%s_used_monitordata' %
                (opts.app_label, opts.model_name)),
            url('^history_data/$',
                admin_site.admin_view(history_data),
                name='%s_%s_history_data' % (opts.app_label, opts.model_name)),
            url('^list-related/(.+)/(.+)/(\d+)/$',
                admin_site.admin_view(self.list_related_view),
                name='%s_%s_list_related' % (opts.app_label, opts.model_name)),
        ] + urls

    def display_used(self, rdata):
        if rdata.used is None:
            return ''
        url = reverse('admin:resources_resourcedata_used_monitordata',
                      args=(rdata.pk, ))
        return format_html('<a href="{}">{} {}</a>', url, rdata.used,
                           rdata.unit)

    display_used.short_description = _("Used")
    display_used.admin_order_field = 'used'

    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
        }
        # Self
        model = apps.get_model(app_name, model_name)
        obj = model.objects.get(id=int(object_id))
        ct_id = resource_models[model]
        qset = Q(content_type_id=ct_id,
                 object_id=obj.id,
                 resource__is_active=True)
        # Related
        for field, rel in obj._meta.fields_map.items():
            try:
                ct_id = resource_models[rel.related_model]
            except KeyError:
                pass
            else:
                manager = getattr(obj, field)
                ids = manager.values_list('id', flat=True)
                qset = Q(qset) | Q(content_type_id=ct_id,
                                   object_id__in=ids,
                                   resource__is_active=True)
        related = ResourceData.objects.filter(qset)
        related_ids = related.values_list('id', flat=True)
        related_ids = ','.join(map(str, related_ids))
        url = reverse('admin:resources_resourcedata_changelist')
        url += '?id__in=%s' % related_ids
        return redirect(url)
Example #27
0
def letsencrypt(modeladmin, request, queryset):
    wildcards = set()
    domains = set()
    content_error = ''
    contentless = queryset.exclude(content__path='/').distinct()
    if contentless:
        content_error = ungettext(
            ugettext(
                "Selected website %s doesn't have a webapp mounted on <tt>/</tt>."
            ),
            ugettext(
                "Selected websites %s don't have a webapp mounted on <tt>/</tt>."
            ),
            len(contentless),
        )
        content_error += ugettext(
            "<br>Websites need a webapp (e.g. static) mounted on </tt>/</tt> "
            "for let's encrypt HTTP-01 challenge to work.")
        content_error = content_error % ', '.join(
            (admin_link()(website) for website in contentless))
        content_error = '<ul class="errorlist"><li>%s</li></ul>' % content_error
    queryset = queryset.prefetch_related('domains')
    for website in queryset:
        for domain in website.domains.all():
            if domain.name.startswith('*.'):
                wildcards.add(domain.name)
            else:
                domains.add(domain.name)
    form = LetsEncryptForm(domains,
                           wildcards,
                           initial={'domains': '\n'.join(domains)})
    action_value = 'letsencrypt'
    if request.POST.get('post') == 'generic_confirmation':
        form = LetsEncryptForm(domains, wildcards, request.POST)
        if not content_error and form.is_valid():
            cleaned_data = form.cleaned_data
            domains = set(cleaned_data['domains'])
            operations = []
            for website in queryset:
                website_domains = [d.name for d in website.domains.all()]
                encrypt_domains = set()
                for domain in domains:
                    if is_valid_domain(domain, website_domains, wildcards):
                        encrypt_domains.add(domain)
                website.encrypt_domains = encrypt_domains
                operations.extend(
                    Operation.create_for_action(website, 'encrypt'))
                modeladmin.log_change(request, website, _("Encrypted!"))
            if not operations:
                messages.error(request,
                               _("No backend operation has been executed."))
            else:
                logs = Operation.execute(operations)
                helpers.message_user(request, logs)
                live_lineages = read_live_lineages(logs)
                errors = 0
                successes = 0
                no_https = 0
                for website in queryset:
                    try:
                        configure_cert(website, live_lineages)
                    except LookupError:
                        errors += 1
                        messages.error(
                            request,
                            _("No lineage found for website %s") %
                            website.name)
                    else:
                        if website.protocol == website.HTTP:
                            no_https += 1
                        website.save(update_fields=('name', ))
                    successes += 1
                context = {
                    'name': website.name,
                    'errors': errors,
                    'successes': successes,
                    'no_https': no_https
                }
                if errors:
                    msg = ungettext(
                        _("No lineages found for websites {name}."),
                        _("No lineages found for {errors} websites."), errors)
                    messages.error(request, msg % context)
                if successes:
                    msg = ungettext(
                        _("{name} website has successfully been encrypted."),
                        _("{successes} websites have been successfully encrypted."
                          ), successes)
                    messages.success(request, msg.format(**context))
                if no_https:
                    msg = ungettext(
                        _("{name} website does not have <b>HTTPS protocol</b> enabled."
                          ),
                        _("{no_https} websites do not have <b>HTTPS protocol</b> enabled."
                          ), no_https)
                    messages.warning(request, mark_safe(msg.format(**context)))
                return
    opts = modeladmin.model._meta
    app_label = opts.app_label
    context = {
        'title':
        _("Let's encrypt!"),
        'action_name':
        _("Encrypt"),
        'content_message':
        ugettext(
            "You are going to request certificates for the following domains.<br>"
            "This operation is safe to run multiple times, "
            "existing certificates will not be regenerated. "
            "Also notice that let's encrypt does not currently support wildcard certificates."
        ) + content_error,
        'action_value':
        action_value,
        'queryset':
        queryset,
        'opts':
        opts,
        'obj':
        website if len(queryset) == 1 else None,
        'app_label':
        app_label,
        'action_checkbox_name':
        admin.helpers.ACTION_CHECKBOX_NAME,
        'form':
        form,
    }
    return TemplateResponse(request,
                            'admin/orchestra/generic_confirmation.html',
                            context)
Example #28
0
class ListAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin,
                ExtendedModelAdmin):
    list_display = ('name', 'address_name', 'address_domain_link',
                    'account_link', 'display_active')
    add_fieldsets = (
        (None, {
            'classes': ('wide', ),
            'fields': ('account_link', 'name', 'is_active')
        }),
        (_("Address"), {
            'classes': ('wide', ),
            'fields': (('address_name', 'address_domain'), )
        }),
        (_("Admin"), {
            'classes': ('wide', ),
            'fields': ('admin_email', 'password1', 'password2'),
        }),
    )
    fieldsets = (
        (None, {
            'classes': ('wide', ),
            'fields': ('account_link', 'name', 'is_active')
        }),
        (_("Address"), {
            'classes': ('wide', ),
            'description':
            _("Additional address besides the default &lt;name&gt;@%s") %
            settings.LISTS_DEFAULT_DOMAIN,
            'fields': (('address_name', 'address_domain'), )
        }),
        (_("Admin"), {
            'classes': ('wide', ),
            'fields': ('password', ),
        }),
    )
    search_fields = ('name', 'address_name', 'address_domain__name',
                     'account__username')
    list_filter = (IsActiveListFilter, HasCustomAddressListFilter)
    readonly_fields = ('account_link', )
    change_readonly_fields = ('name', )
    form = NonStoredUserChangeForm
    add_form = UserCreationForm
    list_select_related = (
        'account',
        'address_domain',
    )
    filter_by_account_fields = ['address_domain']
    actions = (disable, enable, list_accounts)

    address_domain_link = admin_link('address_domain',
                                     order='address_domain__name')

    def get_urls(self):
        useradmin = UserAdmin(List, self.admin_site)
        return [
            url(r'^(\d+)/password/$',
                self.admin_site.admin_view(useradmin.user_change_password))
        ] + super(ListAdmin, self).get_urls()

    def save_model(self, request, obj, form, change):
        """ set password """
        if not change:
            obj.set_password(form.cleaned_data["password1"])
        super(ListAdmin, self).save_model(request, obj, form, change)
Example #29
0
class AddressAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
    list_display = (
        'display_email',
        'account_link',
        'domain_link',
        'display_mailboxes',
        'display_forward',
    )
    list_filter = (HasMailboxListFilter, HasForwardListFilter)
    fields = ('account_link', 'email_link', 'mailboxes', 'forward',
              'display_all_mailboxes')
    add_fields = ('account_link', ('name', 'domain'), 'mailboxes', 'forward')
    #    inlines = [AutoresponseInline]
    search_fields = ('forward', 'mailboxes__name', 'account__username',
                     'computed_email', 'domain__name')
    readonly_fields = ('account_link', 'domain_link', 'email_link',
                       'display_all_mailboxes')
    actions = (SendAddressEmail(), )
    filter_by_account_fields = ('domain', 'mailboxes')
    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():
            url = change_url(mailbox)
            boxes.append('<a href="%s">%s</a>' % (url, mailbox.name))
        return '<br>'.join(boxes)

    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():
            url = change_url(mailbox)
            boxes.append('<a href="%s">%s</a>' % (url, mailbox.name))
        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 = []
        for forward in address.forward.split():
            mbox = forward_mailboxes.get(forward)
            if mbox:
                values.append(admin_link()(mbox))
            else:
                values.append(forward)
        return '<br>'.join(values)

    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)
        if '_to_field' in parse_qs(request.META['QUERY_STRING']):
            # Add address popup
            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:
            if obj.name not in obj.forward.split() and Mailbox.objects.filter(
                    name=obj.name).exists():
                for mailbox in obj.mailboxes.all():
                    if mailbox.name == obj.name:
                        return
                msg = _(
                    "Address '%s' matches mailbox '%s' local address, please consider "
                    "if makes sense adding the mailbox on the mailboxes or forward field."
                ) % (obj, obj.name)
                if msg not in (m.message
                               for m in messages.get_messages(request)):
                    self.message_user(request, msg, level=messages.WARNING)
Example #30
0
 def ticket_id(self, instance):
     return '<b>%s</b>' % admin_link()(instance)
Example #31
0
class MiscellaneousAdmin(SelectPluginAdminMixin, AccountAdminMixin, ExtendedModelAdmin):
    list_display = (
        '__str__', 'service_link', 'amount', 'account_link', 'dispaly_active'
    )
    list_filter = ('service__name', 'is_active')
    list_select_related = ('service', 'account')
    readonly_fields = ('account_link', 'service_link')
    add_fields = ('service', 'account', 'description', 'is_active')
    fields = ('service_link', 'account', 'description', 'is_active')
    change_readonly_fields = ('identifier', 'service')
    search_fields = ('identifier', 'description', 'account__username')
    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'])
        service = self.get_service(obj)
        if obj:
            fields.insert(1, 'account_link')
        if service.has_amount:
            fields.insert(-1, 'amount')
        if service.has_identifier:
            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)()
        else:
            plugin = self.plugin.get(self.plugin_value)()
        self.form = plugin.get_form()
        self.plugin_instance = plugin
        service = self.get_service(obj)
        form = super(SelectPluginAdminMixin, self).get_form(request, obj, **kwargs)
        def clean_identifier(self, service=service):
            identifier = self.cleaned_data['identifier']
            validator_path = settings.MISCELLANEOUS_IDENTIFIER_VALIDATORS.get(service.name, None)
            if validator_path:
                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
            kwargs = {
                plugin.name_field: self.plugin_value
            }
            setattr(obj, self.plugin_field, plugin.model.objects.get(**kwargs))
        obj.save()
Example #32
0
 def instance_link(self, operation):
     link = admin_link('instance')(self, operation)
     if link == '---':
         return _("Deleted {0}").format(operation.instance_repr or '-'.join(
             (escape(operation.content_type), escape(operation.object_id))))
     return link
Example #33
0
class BillAdmin(BillAdminMixin, ExtendedModelAdmin):
    list_display = ('number', 'type_link', 'account_link', 'closed_on_display',
                    'updated_on_display', 'num_lines', 'display_total',
                    'display_payment_state', 'is_sent')
    list_filter = (
        BillTypeListFilter,
        'is_open',
        'is_sent',
        TotalListFilter,
        PaymentStateListFilter,
        AmendedListFilter,
        'account__is_active',
    )
    add_fields = ('account', 'type', 'amend_of', 'is_open', 'due_on',
                  'comments')
    change_list_template = 'admin/bills/bill/change_list.html'
    fieldsets = (
        (None, {
            'fields': [
                'number', 'type', (), 'account_link',
                'display_total_with_subtotals', 'display_payment_state',
                'is_sent', 'comments'
            ],
        }),
        (_("Dates"), {
            'classes': ('collapse', ),
            'fields': ('created_on_display', 'closed_on_display',
                       'updated_on_display', 'due_on'),
        }),
        (_("Raw"), {
            'classes': ('collapse', ),
            'fields': ('html', ),
        }),
    )
    list_prefetch_related = ('transactions', 'lines__sublines')
    search_fields = ('number', 'account__username', 'comments')
    change_view_actions = [
        actions.manage_lines,
        actions.view_bill,
        actions.download_bills,
        actions.send_bills,
        actions.close_bills,
        actions.amend_bills,
        actions.close_send_download_bills,
    ]
    actions = [
        actions.manage_lines,
        actions.download_bills,
        actions.close_bills,
        actions.send_bills,
        actions.amend_bills,
        actions.bill_report,
        actions.service_report,
        actions.close_send_download_bills,
        list_accounts,
    ]
    change_readonly_fields = ('account_link', 'type', 'is_open',
                              'amend_of_link')
    readonly_fields = (
        'number',
        'display_total',
        'is_sent',
        'display_payment_state',
        'created_on_display',
        '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():
    #            url = reverse('admin:bills_bill_change', args=(amend.id,))
    #            links.append('<a href="{url}">{num}</a>'.format(url=url, num=amend.number))
    #        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)
        return '<a href="%s">%s</a>' % (url, bill.get_type_display())

    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()
        admin_site = self.admin_site
        extra_urls = [
            url("^manage-lines/$",
                admin_site.admin_view(
                    BillLineManagerAdmin(BillLine,
                                         admin_site).changelist_view),
                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:
            # Switches between amend_of_link and amend_links fields
            fields = fieldsets[0][1]['fields']
            if obj.amend_of_id:
                fields[2] = 'amend_of_link'
            else:
                fields[2] = ()
            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 = []
        if obj:
            if not obj.is_open:
                exclude += ['close_bills', 'close_send_download_bills']
            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:
            if obj.amends.all():
                cls.inlines = [AmendInline, ClosedBillLineInline]
            else:
                cls.inlines = [ClosedBillLineInline]
        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':
            kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 4})
        elif db_field.name == 'html':
            kwargs['widget'] = forms.Textarea(attrs={'cols': 150, 'rows': 20})
        formfield = super().formfield_for_dbfield(db_field, **kwargs)
        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))
        actions.validate_contact(request, bill, error=False)
        return super().change_view(request, object_id, **kwargs)
Example #34
0
def letsencrypt(modeladmin, request, queryset):
    wildcards = set()
    domains = set()
    content_error = ''
    contentless = queryset.exclude(content__path='/').distinct()
    if contentless:
        content_error = ungettext(
            ugettext("Selected website %s doesn't have a webapp mounted on <tt>/</tt>."),
            ugettext("Selected websites %s don't have a webapp mounted on <tt>/</tt>."),
            len(contentless),
        )
        content_error += ugettext("<br>Websites need a webapp (e.g. static) mounted on </tt>/</tt> "
                                  "for let's encrypt HTTP-01 challenge to work.")
        content_error = content_error % ', '.join((admin_link()(website) for website in contentless))
        content_error = '<ul class="errorlist"><li>%s</li></ul>' % content_error
    queryset = queryset.prefetch_related('domains')
    for website in queryset:
        for domain in website.domains.all():
            if domain.name.startswith('*.'):
                wildcards.add(domain.name)
            else:
                domains.add(domain.name)
    form = LetsEncryptForm(domains, wildcards, initial={'domains': '\n'.join(domains)})
    action_value = 'letsencrypt'
    if request.POST.get('post') == 'generic_confirmation':
        form = LetsEncryptForm(domains, wildcards, request.POST)
        if not content_error and form.is_valid():
            cleaned_data = form.cleaned_data
            domains = set(cleaned_data['domains'])
            operations = []
            for website in queryset:
                website_domains = [d.name for d in website.domains.all()]
                encrypt_domains = set()
                for domain in domains:
                    if is_valid_domain(domain, website_domains, wildcards):
                        encrypt_domains.add(domain)
                website.encrypt_domains = encrypt_domains
                operations.extend(Operation.create_for_action(website, 'encrypt'))
                modeladmin.log_change(request, website, _("Encrypted!"))
            if not operations:
                messages.error(request, _("No backend operation has been executed."))
            else:
                logs = Operation.execute(operations)
                helpers.message_user(request, logs)
                live_lineages = read_live_lineages(logs)
                errors = 0
                successes = 0
                no_https = 0
                for website in queryset:
                    try:
                        configure_cert(website, live_lineages)
                    except LookupError:
                        errors += 1
                        messages.error(request, _("No lineage found for website %s") % website.name)
                    else:
                        if website.protocol == website.HTTP:
                            no_https += 1
                        website.save(update_fields=('name',))
                    successes += 1
                context = {
                    'name': website.name,
                    'errors': errors,
                    'successes': successes,
                    'no_https': no_https
                }
                if errors:
                    msg = ungettext(
                        _("No lineages found for websites {name}."),
                        _("No lineages found for {errors} websites."),
                        errors)
                    messages.error(request, msg % context)
                if successes:
                    msg = ungettext(
                        _("{name} website has successfully been encrypted."),
                        _("{successes} websites have been successfully encrypted."),
                        successes)
                    messages.success(request, msg.format(**context))
                if no_https:
                    msg = ungettext(
                        _("{name} website does not have <b>HTTPS protocol</b> enabled."),
                        _("{no_https} websites do not have <b>HTTPS protocol</b> enabled."),
                        no_https)
                    messages.warning(request, mark_safe(msg.format(**context)))
                return
    opts = modeladmin.model._meta
    app_label = opts.app_label
    context = {
        'title': _("Let's encrypt!"),
        'action_name': _("Encrypt"),
        'content_message': ugettext("You are going to request certificates for the following domains.<br>"
            "This operation is safe to run multiple times, "
            "existing certificates will not be regenerated. "
            "Also notice that let's encrypt does not currently support wildcard certificates.") + content_error,
        'action_value': action_value,
        'queryset': queryset,
        'opts': opts,
        'obj': website if len(queryset) == 1 else None,
        'app_label': app_label,
        'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
        'form': form,
    }
    return TemplateResponse(request, 'admin/orchestra/generic_confirmation.html', context)
Example #35
0
 def bills_links(self, order):
     bills = []
     make_link = admin_link()
     for line in order.lines.select_related('bill').distinct('bill'):
         bills.append(make_link(line.bill))
     return '<br>'.join(bills)
Example #36
0
class OrderAdmin(AccountAdminMixin, ExtendedModelAdmin):
    list_display = ('display_description', 'service_link', 'account_link',
                    'content_object_link', 'display_registered_on',
                    'display_billed_until', 'display_cancelled_on',
                    'display_metric')
    list_filter = (
        ActiveOrderListFilter,
        IgnoreOrderListFilter,
        BilledOrderListFilter,
        'account__type',
        'service',
    )
    default_changelist_filters = (('ignore', '0'), )
    actions = (BillSelectedOrders(), mark_as_ignored, mark_as_not_ignored,
               report, list_accounts)
    change_view_actions = (BillSelectedOrders(), mark_as_ignored,
                           mark_as_not_ignored)
    date_hierarchy = 'registered_on'
    inlines = (MetricStorageInline, )
    add_inlines = ()
    search_fields = (
        'account__username',
        'content_object_repr',
        'description',
    )
    list_prefetch_related = (
        'content_object',
        Prefetch('metrics', queryset=MetricStorage.objects.order_by('-id')),
    )
    list_select_related = ('account', 'service')
    add_fieldsets = (
        (None, {
            'fields': ('account', 'service')
        }),
        (_("Object"), {
            'fields': (
                'content_type',
                'object_id',
            ),
        }),
        (_("State"), {
            'fields': ('registered_on', 'cancelled_on', 'billed_on',
                       'billed_metric', 'billed_until')
        }),
        (None, {
            'fields': (
                'description',
                'ignore',
            ),
        }),
    )
    fieldsets = (
        (None, {
            'fields': ('account_link', 'service_link', 'content_object_link'),
        }),
        (_("State"), {
            'fields': ('registered_on', 'cancelled_on', 'billed_on',
                       'billed_metric', 'billed_until')
        }),
        (None, {
            'fields': ('description', 'ignore', 'bills_links'),
        }),
    )
    readonly_fields = ('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:
                url = change_url(order.content_object)
            except NoReverseMatch:
                # Does not has admin
                return order.content_object_repr
            description = str(order.content_object)
            return '<a href="{url}">{description}</a>'.format(
                url=url, description=description)
        return order.content_object_repr

    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()
        for line in order.lines.select_related('bill').distinct('bill'):
            bills.append(make_link(line.bill))
        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
        human = escape(naturaldate(billed_until))
        if billed_until:
            if order.cancelled_on and order.cancelled_on <= billed_until:
                pass
            elif order.service.billing_period == order.service.NEVER:
                human = _("Forever")
            elif order.service.payment_style == order.service.POSTPAY:
                boundary = order.service.handler.get_billing_point(order)
                if billed_until < boundary:
                    red = True
            elif billed_until < timezone.now().date():
                red = True
        color = 'style="color:red;"' if red else ''
        return '<span title="{raw}" {color}>{human}</span>'.format(
            raw=escape(str(billed_until)),
            color=color,
            human=human,
        )

    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
        """
        try:
            metric = order.metrics.all()[0]
        except IndexError:
            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':
            kwargs['widget'] = forms.Textarea(attrs={'cols': 70, 'rows': 2})
        return super().formfield_for_dbfield(db_field, **kwargs)
Example #37
0
class DomainAdmin(AccountAdminMixin, ExtendedModelAdmin):
    list_display = ('structured_name', 'display_is_top', 'display_websites',
                    'display_addresses', 'account_link')
    add_fields = ('name', 'account')
    fields = ('name', 'account_link', 'display_websites', 'display_addresses',
              'dns2136_address_match_list')
    readonly_fields = ('account_link', 'top_link', 'display_websites',
                       'display_addresses', 'implicit_records')
    inlines = (RecordInline, DomainInline)
    list_filter = (TopDomainListFilter, HasWebsiteFilter, HasAddressFilter)
    change_readonly_fields = ('name', 'serial')
    search_fields = ('name', 'account__username', 'records__value')
    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
        return mark_safe('&nbsp;' * 4 + domain.name)

    structured_name.short_description = _("name")
    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'

    @mark_safe
    def display_websites(self, domain):
        if apps.isinstalled('orchestra.contrib.websites'):
            websites = domain.websites.all()
            if websites:
                links = []
                for website in websites:
                    site_link = get_on_site_link(website.get_absolute_url())
                    admin_url = change_url(website)
                    title = _("Edit website")
                    link = format_html('<a href="{}" title="{}">{} {}</a>',
                                       admin_url, title, website.name,
                                       site_link)
                    links.append(link)
                return '<br>'.join(links)
            add_url = reverse('admin:websites_website_add')
            add_url += '?account=%i&domains=%i' % (domain.account_id,
                                                   domain.pk)
            add_link = format_html(
                '<a href="{}" title="{}"><img src="{}" /></a>',
                add_url,
                _("Add website"),
                static('orchestra/images/add.png'),
            )
            return _("No website %s") % (add_link)
        return '---'

    display_websites.admin_order_field = 'websites__name'
    display_websites.short_description = _("Websites")

    @mark_safe
    def display_addresses(self, domain):
        if apps.isinstalled('orchestra.contrib.mailboxes'):
            add_url = reverse('admin:mailboxes_address_add')
            add_url += '?account=%i&domain=%i' % (domain.account_id, domain.pk)
            image = '<img src="%s"></img>' % static('orchestra/images/add.png')
            add_link = '<a href="%s" title="%s">%s</a>' % (
                add_url, _("Add address"), image)
            addresses = domain.addresses.all()
            if addresses:
                url = reverse('admin:mailboxes_address_changelist')
                url += '?domain=%i' % addresses[0].domain_id
                title = '\n'.join([address.email for address in addresses])
                return '<a href="%s" title="%s">%s</a> %s' % (
                    url, title, len(addresses), add_link)
            return _("No address %s") % (add_link)
        return '---'

    display_addresses.short_description = _("Addresses")
    display_addresses.admin_order_field = 'addresses__count'

    @mark_safe
    def implicit_records(self, domain):
        types = set(domain.records.values_list('type', flat=True))
        ttl = settings.DOMAINS_DEFAULT_TTL
        lines = []
        for record in domain.get_default_records():
            line = '{name} {ttl} IN {type} {value}'.format(name=domain.name,
                                                           ttl=ttl,
                                                           type=record.type,
                                                           value=record.value)
            if not domain.record_is_implicit(record, types):
                line = format_html('<strike>{}</strike>', line)
            if record.type is Record.SOA:
                lines.insert(0, line)
            else:
                lines.append(line)
        return '<br>'.join(lines)

    implicit_records.short_description = _("Implicit records")

    def get_fieldsets(self, request, obj=None):
        """ Add SOA fields when domain is top """
        fieldsets = super(DomainAdmin, self).get_fieldsets(request, obj)
        if obj:
            fieldsets += ((_("Implicit records"), {
                'classes': ('collapse', ),
                'fields': ('implicit_records', ),
            }), )
            if obj.is_top:
                fieldsets += ((_("SOA"), {
                    'classes': ('collapse', ),
                    'description':
                    _("SOA (Start of Authority) records are used to determine how the "
                      "zone propagates to the secondary nameservers."),
                    'fields':
                    ('serial', 'refresh', 'retry', 'expire', 'min_ttl'),
                }), )
            else:
                existing = fieldsets[0][1]['fields']
                if 'top_link' not in existing:
                    fieldsets[0][1]['fields'].insert(2, 'top_link')
        return fieldsets

    def get_inline_instances(self, request, obj=None):
        inlines = super(DomainAdmin, self).get_inline_instances(request, obj)
        if not obj or not obj.is_top:
            return [
                inline for inline in inlines if type(inline) != DomainInline
            ]
        return inlines

    def get_queryset(self, request):
        """ Order by structured name and imporve performance """
        qs = super(DomainAdmin, self).get_queryset(request)
        qs = qs.select_related('top', 'account')
        if request.method == 'GET':
            qs = qs.annotate(structured_id=Coalesce('top__id', 'id'),
                             structured_name=Concat('top__name',
                                                    'name')).order_by(
                                                        '-structured_id',
                                                        'structured_name')
        if apps.isinstalled('orchestra.contrib.websites'):
            qs = qs.prefetch_related('websites__domains')
        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)
        self.extra_domains = []
        if not change:
            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)
        if not change:
            # Clone records to extra_domains, if any
            for formset in formsets:
                if formset.model is Record:
                    for domain in self.extra_domains:
                        # Reset pk value of the record instances to force creation of new ones
                        for record_form in formset.forms:
                            record = record_form.instance
                            if record.pk:
                                record.pk = None
                        formset.instance = domain
                        form.instance = domain
                        self.save_formset(request, form, formset, change)
Example #38
0
class LogEntryAdmin(admin.ModelAdmin):
    list_display = (
        'display_action_time',
        'user_link',
        'display_message',
    )
    list_filter = (
        'action_flag',
        ('user', admin.RelatedOnlyFieldListFilter),
        ('content_type', admin.RelatedOnlyFieldListFilter),
    )
    date_hierarchy = 'action_time'
    search_fields = ('object_repr', 'change_message', 'user__username')
    fields = ('user_link', 'content_object_link', 'display_action_time',
              'display_action', 'change_message')
    readonly_fields = (
        'user_link',
        'content_object_link',
        'display_action_time',
        'display_action',
    )
    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"))

    @mark_safe
    def display_message(self, log):
        edit = format_html(
            '<a href="{url}"><img src="{img}"></img></a>', **{
                'url': reverse('admin:admin_logentry_change', args=(log.pk, )),
                'img': static('admin/img/icon-changelink.svg'),
            })
        if log.is_addition():
            return _('Added "%(link)s". %(edit)s') % {
                'link': self.content_object_link(log),
                'edit': edit
            }
        elif log.is_change():
            return _('Changed "%(link)s" - %(changes)s %(edit)s') % {
                'link': self.content_object_link(log),
                'changes': log.get_change_message(),
                'edit': edit,
            }
        elif log.is_deletion():
            return _('Deleted "%(object)s." %(edit)s') % {
                'object': log.object_repr,
                'edit': edit,
            }

    display_message.short_description = _("Message")
    display_message.admin_order_field = 'action_flag'

    def display_action(self, log):
        if log.is_addition():
            return _("Added")
        elif log.is_change():
            return _("Changed")
        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)
        try:
            url = reverse(view, args=(log.object_id, ))
        except NoReverseMatch:
            return log.object_repr
        return format_html('<a href="{}">{}</a>', url, log.object_repr)

    content_object_link.short_description = _("Content object")
    content_object_link.admin_order_field = 'object_repr'

    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():
            context.update({
                'rel_opts': obj.content_type.model_class()._meta,
                'object': obj,
            })
        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():
            opts = obj.content_type.model_class()._meta
            view = 'admin:%s_%s_history' % (opts.app_label, opts.model_name)
            post_url = reverse(view, args=(obj.object_id, ))
            preserved_filters = self.get_preserved_filters(request)
            post_url = add_preserved_filters(
                {
                    'preserved_filters': preserved_filters,
                    'opts': opts
                }, 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
Example #39
0
 def account_link(self, instance):
     account = instance.account if instance.pk else self.account
     return admin_link()(account)
Example #40
0
class BillLineAdmin(admin.ModelAdmin):
    list_display = ('description', 'bill_link', 'display_is_open',
                    'account_link', 'rate', 'quantity', 'tax', 'subtotal',
                    'display_sublinetotal', 'display_total')
    actions = (
        actions.undo_billing,
        actions.move_lines,
        actions.copy_lines,
        actions.service_report,
        actions.list_bills,
    )
    fieldsets = (
        (None, {
            'fields': ('bill_link', 'description', 'tax', 'start_on', 'end_on',
                       'amended_line_link')
        }),
        (_("Totals"), {
            'fields': ('rate', ('quantity', 'verbose_quantity'), 'subtotal',
                       'display_sublinetotal', 'display_total'),
        }),
        (_("Order"), {
            'fields': (
                'order_link',
                'order_billed_on',
                'order_billed_until',
            )
        }),
    )
    readonly_fields = ('bill_link', 'order_link', 'amended_line_link',
                       'display_sublinetotal', 'display_total')
    list_filter = ('tax', 'bill__is_open', 'order__service')
    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:
            return list(fields) + [
                'description', 'tax', 'start_on', 'end_on', 'rate', 'quantity',
                'verbose_quantity', 'subtotal', 'order_billed_on',
                'order_billed_until'
            ]
        return fields

    def get_queryset(self, request):
        qs = super().get_queryset(request)
        qs = qs.annotate(
            subline_total=Sum('sublines__total'),
            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
        return super().has_delete_permission(request, obj)
Example #41
0
 def account_link(self, instance):
     account = instance.account if instance.pk else self.account
     return admin_link()(account)
Example #42
0
class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin,
                   ExtendedModelAdmin):
    list_display = ('username', 'full_name', 'type', 'is_active')
    list_filter = (
        'type',
        'is_active',
    )
    add_fieldsets = (
        (_("User"), {
            'fields': (
                'username',
                'password1',
                'password2',
            ),
        }),
        (_("Personal info"), {
            'fields': ('short_name', 'full_name', 'email',
                       ('type', 'language'), 'comments'),
        }),
        (_("Permissions"), {
            'fields': ('is_superuser', )
        }),
    )
    fieldsets = (
        (_("User"), {
            'fields': ('username', 'password', 'main_systemuser_link')
        }),
        (_("Personal info"), {
            'fields': ('short_name', 'full_name', 'email',
                       ('type', 'language'), 'comments'),
        }),
        (_("Permissions"), {
            'fields': ('is_superuser', 'is_active')
        }),
        (_("Important dates"), {
            'classes': ('collapse', ),
            'fields': ('last_login', 'date_joined')
        }),
    )
    search_fields = ('username', 'short_name', 'full_name')
    add_form = AccountCreationForm
    form = UserChangeForm
    filter_horizontal = ()
    change_readonly_fields = ('username', 'main_systemuser_link', 'is_active')
    change_form_template = 'admin/accounts/account/change_form.html'
    actions = (disable_selected, enable_selected, delete_related_services,
               list_contacts, service_report, SendEmail())
    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:
                messages.warning(request, 'This account is disabled.')
            context.update({
                'services':
                sorted([
                    model._meta
                    for model in services.get() if model is not Account
                ],
                       key=lambda i: i.verbose_name_plural.lower()),
                'accounts':
                sorted([
                    model._meta
                    for model in accounts.get() if model is not Account
                ],
                       key=lambda i: i.verbose_name_plural.lower())
            })
        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:
            fields = AccountCreationForm.create_related_fields
            if fields:
                fieldsets = copy.deepcopy(fieldsets)
                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)
            form.save_related(obj)
        else:
            if isinstalled('orchestra.contrib.orders') and isinstalled(
                    'orchestra.contrib.services'):
                if 'type' in form.changed_data:
                    old_type = Account.objects.get(pk=obj.pk).type
                    new_type = form.cleaned_data['type']
                    context = {
                        'from': old_type.lower(),
                        'to': new_type.lower(),
                        'url': reverse('admin:orders_order_changelist'),
                    }
                    msg = ''
                    if old_type in SERVICES_IGNORE_ACCOUNT_TYPE and new_type not in SERVICES_IGNORE_ACCOUNT_TYPE:
                        context['url'] += '?account=%i&ignore=1' % obj.pk
                        msg = _(
                            "Account type has been changed from <i>%(from)s</i> to <i>%(to)s</i>. "
                            "You may want to mark <a href='%(url)s'>existing ignored orders</a> as not ignored."
                        )
                    elif old_type not in SERVICES_IGNORE_ACCOUNT_TYPE and new_type in SERVICES_IGNORE_ACCOUNT_TYPE:
                        context['url'] += '?account=%i&ignore=0' % obj.pk
                        msg = _(
                            "Account type has been changed from <i>%(from)s</i> to <i>%(to)s</i>. "
                            "You may want to ignore <a href='%(url)s'>existing not ignored orders</a>."
                        )
                    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:
            if obj.is_active:
                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:
            del actions['delete_selected']
        return actions
Example #43
0
 def bills_links(self, order):
     bills = []
     make_link = admin_link()
     for line in order.lines.select_related('bill').distinct('bill'):
         bills.append(make_link(line.bill))
     return '<br>'.join(bills)