Implemented enable admin action0
This commit is contained in:
parent
6a34ba8fd2
commit
4adfd4c83a
8
TODO.md
8
TODO.md
|
@ -437,3 +437,11 @@ mkhomedir_helper or create ssh homes with bash.rc and such
|
||||||
# Warning websites with ssl options without https protocol
|
# Warning websites with ssl options without https protocol
|
||||||
|
|
||||||
# Schedule cancellation
|
# Schedule cancellation
|
||||||
|
|
||||||
|
# Multiple domains wordpress
|
||||||
|
|
||||||
|
# TODO: separate ports for fpm version
|
||||||
|
|
||||||
|
# Reversion
|
||||||
|
# implement re-enable account
|
||||||
|
# Disable/enable saas and VPS
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.core.mail import send_mass_mail
|
from django.core.mail import send_mass_mail
|
||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
|
@ -108,23 +110,36 @@ class SendEmail(object):
|
||||||
return render(request, self.template, self.context)
|
return render(request, self.template, self.context)
|
||||||
|
|
||||||
|
|
||||||
@action_with_confirmation()
|
def base_disable(modeladmin, request, queryset, disable=True):
|
||||||
def disable(modeladmin, request, queryset):
|
|
||||||
num = 0
|
num = 0
|
||||||
|
action_name = _("disabled") if disable else _("enabled")
|
||||||
for obj in queryset:
|
for obj in queryset:
|
||||||
obj.disable()
|
obj.disable() if disable else obj.enable()
|
||||||
modeladmin.log_change(request, obj, _("Disabled"))
|
modeladmin.log_change(request, obj, action_name.capitalize())
|
||||||
num += 1
|
num += 1
|
||||||
opts = modeladmin.model._meta
|
opts = modeladmin.model._meta
|
||||||
context = {
|
context = {
|
||||||
|
'action_name': action_name,
|
||||||
'verbose_name': opts.verbose_name,
|
'verbose_name': opts.verbose_name,
|
||||||
'verbose_name_plural': opts.verbose_name_plural,
|
'verbose_name_plural': opts.verbose_name_plural,
|
||||||
'num': num
|
'num': num
|
||||||
}
|
}
|
||||||
msg = ungettext(
|
msg = ungettext(
|
||||||
_("Selected %(verbose_name)s and related services has been disabled.") % context,
|
_("Selected %(verbose_name)s and related services has been %(action_name)s.") % context,
|
||||||
_("%(num)s selected %(verbose_name_plural)s and related services have been disabled.") % context,
|
_("%(num)s selected %(verbose_name_plural)s and related services have been %(action_name)s.") % context,
|
||||||
num)
|
num)
|
||||||
modeladmin.message_user(request, msg)
|
modeladmin.message_user(request, msg)
|
||||||
|
|
||||||
|
|
||||||
|
@action_with_confirmation()
|
||||||
|
def disable(modeladmin, request, queryset):
|
||||||
|
return base_disable(modeladmin, request, queryset)
|
||||||
disable.url_name = 'disable'
|
disable.url_name = 'disable'
|
||||||
disable.short_description = _("Disable")
|
disable.short_description = _("Disable")
|
||||||
|
|
||||||
|
|
||||||
|
@action_with_confirmation()
|
||||||
|
def enable(modeladmin, request, queryset):
|
||||||
|
return base_disable(modeladmin, request, queryset, disable=False)
|
||||||
|
enable.url_name = 'enable'
|
||||||
|
enable.short_description = _("Enable")
|
||||||
|
|
|
@ -149,7 +149,7 @@ class ChangeViewActionsMixin(object):
|
||||||
kwargs['extra_context']['object_tools_items'] = [
|
kwargs['extra_context']['object_tools_items'] = [
|
||||||
action.__dict__ for action in self.get_change_view_actions(obj)
|
action.__dict__ for action in self.get_change_view_actions(obj)
|
||||||
]
|
]
|
||||||
return super(ChangeViewActionsMixin, self).change_view(request, object_id, **kwargs)
|
return super().change_view(request, object_id, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class ChangeAddFieldsMixin(object):
|
class ChangeAddFieldsMixin(object):
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from functools import partial, wraps
|
||||||
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.contrib.admin import helpers
|
from django.contrib.admin import helpers
|
||||||
from django.contrib.admin.utils import NestedObjects, quote
|
from django.contrib.admin.utils import NestedObjects, quote
|
||||||
|
@ -186,21 +188,21 @@ def delete_related_services(modeladmin, request, queryset):
|
||||||
delete_related_services.short_description = _("Delete related services")
|
delete_related_services.short_description = _("Delete related services")
|
||||||
|
|
||||||
|
|
||||||
def disable_selected(modeladmin, request, queryset):
|
def disable_selected(modeladmin, request, queryset, disable=True):
|
||||||
opts = modeladmin.model._meta
|
opts = modeladmin.model._meta
|
||||||
app_label = opts.app_label
|
app_label = opts.app_label
|
||||||
|
verbose_action_name = _("disabled") if disable else _("enabled")
|
||||||
# The user has already confirmed the deletion.
|
# The user has already confirmed the deletion.
|
||||||
# Do the disable and return a None to display the change list view again.
|
# Do the disable and return a None to display the change list view again.
|
||||||
if request.POST.get('post'):
|
if request.POST.get('post'):
|
||||||
n = 0
|
n = 0
|
||||||
for account in queryset:
|
for account in queryset:
|
||||||
account.disable()
|
account.disable() if disable else account.enable()
|
||||||
modeladmin.log_change(request, account, _("Disabled"))
|
modeladmin.log_change(request, account, verbose_action_name.capitalize())
|
||||||
n += 1
|
n += 1
|
||||||
modeladmin.message_user(request, ungettext(
|
modeladmin.message_user(request, ungettext(
|
||||||
_("One account has been successfully disabled."),
|
_("One account has been successfully %s.") % verbose_action_name,
|
||||||
_("%i accounts have been successfully disabled.") % n,
|
_("%i accounts have been successfully %s.") % (n, verbose_action_name),
|
||||||
n)
|
n)
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
@ -248,6 +250,8 @@ def disable_selected(modeladmin, request, queryset):
|
||||||
|
|
||||||
context = dict(
|
context = dict(
|
||||||
admin_site.each_context(request),
|
admin_site.each_context(request),
|
||||||
|
action_name='disable_selected' if disable else 'enable_selected',
|
||||||
|
disable=disable,
|
||||||
title=_("Are you sure?"),
|
title=_("Are you sure?"),
|
||||||
objects_name=objects_name,
|
objects_name=objects_name,
|
||||||
deletable_objects=display,
|
deletable_objects=display,
|
||||||
|
@ -259,5 +263,11 @@ def disable_selected(modeladmin, request, queryset):
|
||||||
template = 'admin/%s/%s/disable_selected_confirmation.html' % (app_label, opts.model_name)
|
template = 'admin/%s/%s/disable_selected_confirmation.html' % (app_label, opts.model_name)
|
||||||
return TemplateResponse(request, template, context)
|
return TemplateResponse(request, template, context)
|
||||||
disable_selected.short_description = _("Disable selected accounts")
|
disable_selected.short_description = _("Disable selected accounts")
|
||||||
disable_selected.url = 'disable'
|
disable_selected.url_name = 'disable'
|
||||||
disable_selected.tool_description = _("Disable")
|
disable_selected.tool_description = _("Disable")
|
||||||
|
|
||||||
|
|
||||||
|
enable_selected = partial(disable_selected, disable=False)
|
||||||
|
enable_selected.__name__ = 'enable_selected'
|
||||||
|
enable_selected.url_name = 'enable'
|
||||||
|
enable_selected.tool_description = _("Enable")
|
||||||
|
|
|
@ -20,7 +20,8 @@ from orchestra.admin.utils import wrap_admin_view, admin_link, set_url_query
|
||||||
from orchestra.core import services, accounts
|
from orchestra.core import services, accounts
|
||||||
from orchestra.forms import UserChangeForm
|
from orchestra.forms import UserChangeForm
|
||||||
|
|
||||||
from .actions import list_contacts, service_report, delete_related_services, disable_selected
|
from .actions import (list_contacts, service_report, delete_related_services, disable_selected,
|
||||||
|
enable_selected)
|
||||||
from .filters import HasMainUserListFilter
|
from .filters import HasMainUserListFilter
|
||||||
from .forms import AccountCreationForm
|
from .forms import AccountCreationForm
|
||||||
from .models import Account
|
from .models import Account
|
||||||
|
@ -64,9 +65,10 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
||||||
change_readonly_fields = ('username', 'main_systemuser_link', 'is_active')
|
change_readonly_fields = ('username', 'main_systemuser_link', 'is_active')
|
||||||
change_form_template = 'admin/accounts/account/change_form.html'
|
change_form_template = 'admin/accounts/account/change_form.html'
|
||||||
actions = (
|
actions = (
|
||||||
disable_selected, delete_related_services, list_contacts, service_report, SendEmail()
|
disable_selected, enable_selected, delete_related_services, list_contacts, service_report,
|
||||||
|
SendEmail()
|
||||||
)
|
)
|
||||||
change_view_actions = (disable_selected, service_report)
|
change_view_actions = (disable_selected, service_report, enable_selected)
|
||||||
ordering = ()
|
ordering = ()
|
||||||
|
|
||||||
main_systemuser_link = admin_link('main_systemuser')
|
main_systemuser_link = admin_link('main_systemuser')
|
||||||
|
@ -111,6 +113,14 @@ class AccountAdmin(ChangePasswordAdminMixin, auth.UserAdmin, ExtendedModelAdmin)
|
||||||
else:
|
else:
|
||||||
super(AccountAdmin, self).save_model(request, obj, form, change)
|
super(AccountAdmin, self).save_model(request, obj, form, change)
|
||||||
|
|
||||||
|
def get_change_view_actions(self, obj=None):
|
||||||
|
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):
|
def get_actions(self, request):
|
||||||
actions = super(AccountAdmin, self).get_actions(request)
|
actions = super(AccountAdmin, self).get_actions(request)
|
||||||
if 'delete_selected' in actions:
|
if 'delete_selected' in actions:
|
||||||
|
|
|
@ -83,6 +83,11 @@ class Account(auth.AbstractBaseUser):
|
||||||
self.save(update_fields=('is_active',))
|
self.save(update_fields=('is_active',))
|
||||||
self.notify_related()
|
self.notify_related()
|
||||||
|
|
||||||
|
def enable(self):
|
||||||
|
self.is_active = True
|
||||||
|
self.save(update_fields=('is_active',))
|
||||||
|
self.notify_related()
|
||||||
|
|
||||||
def get_services_to_disable(self):
|
def get_services_to_disable(self):
|
||||||
for rel in self._meta.get_all_related_objects():
|
for rel in self._meta.get_all_related_objects():
|
||||||
source = getattr(rel, 'related_model', rel.model)
|
source = getattr(rel, 'related_model', rel.model)
|
||||||
|
|
|
@ -8,27 +8,14 @@
|
||||||
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
|
||||||
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
› <a href="{% url 'admin:app_list' app_label=opts.app_label %}">{{ opts.app_config.verbose_name }}</a>
|
||||||
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
|
› <a href="{% url opts|admin_urlname:'changelist' %}">{{ opts.verbose_name_plural|capfirst }}</a>
|
||||||
› {% trans 'Disable accounts' %}
|
› {% if disable%}{% blocktrans %}Disable {{ objects_name }}{% endblocktrans %}{% else %}{% blocktrans %}Enable {{ objects_name }}{% endblocktrans %}{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% if perms_lacking %}
|
{% if disable%}<p>{% blocktrans %}Are you sure you want to disable selected {{ objects_name }}?{% endblocktrans %}</p>
|
||||||
<p>{% blocktrans %}Disabling the selected {{ objects_name }} would result in disabling related objects, but your account doesn't have permission to disable the following types of objects:{% endblocktrans %}</p>
|
{% else %}<p>{% blocktrans %}Are you sure you want to enable selected {{ objects_name }}?{% endblocktrans %}</p>
|
||||||
<ul>
|
{% endif %}
|
||||||
{% for obj in perms_lacking %}
|
|
||||||
<li>{{ obj }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% elif protected %}
|
|
||||||
<p>{% blocktrans %}Deleting the selected {{ objects_name }} would require deleting the following protected related objects:{% endblocktrans %}</p>
|
|
||||||
<ul>
|
|
||||||
{% for obj in protected %}
|
|
||||||
<li>{{ obj }}</li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
{% else %}
|
|
||||||
<p>{% blocktrans %}Are you sure you want to disable the selected {{ objects_name }}? All of the following objects and their related items will be disabled:{% endblocktrans %}</p>
|
|
||||||
<h2>{% trans "Objects" %}</h2>
|
<h2>{% trans "Objects" %}</h2>
|
||||||
{% for deletable_object in deletable_objects %}
|
{% for deletable_object in deletable_objects %}
|
||||||
<ul>{{ deletable_object|unordered_list }}</ul>
|
<ul>{{ deletable_object|unordered_list }}</ul>
|
||||||
|
@ -38,12 +25,11 @@
|
||||||
{% for obj in queryset %}
|
{% for obj in queryset %}
|
||||||
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
<input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk|unlocalize }}" />
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<input type="hidden" name="action" value="disable_selected" />
|
<input type="hidden" name="action" value="{{ action_name }}" />
|
||||||
<input type="hidden" name="post" value="yes" />
|
<input type="hidden" name="post" value="yes" />
|
||||||
<input type="submit" value="{% trans "Yes, I'm sure" %}" />
|
<input type="submit" value="{% trans "Yes, I'm sure" %}" />
|
||||||
<a href="#" onclick="window.history.back(); return false;" class="button cancel-link">{% trans "No, take me back" %}</a>
|
<a href="#" onclick="window.history.back(); return false;" class="button cancel-link">{% trans "No, take me back" %}</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -292,7 +292,7 @@ class BillAdmin(AccountAdminMixin, ExtendedModelAdmin):
|
||||||
state = bill.get_payment_state_display().upper()
|
state = bill.get_payment_state_display().upper()
|
||||||
title = ''
|
title = ''
|
||||||
if bill.closed_amends:
|
if bill.closed_amends:
|
||||||
state += '*'
|
state = '<strike>%s*</strike>' % state
|
||||||
title = _("This bill has been amended, this value may not be valid.")
|
title = _("This bill has been amended, this value may not be valid.")
|
||||||
color = PAYMENT_STATE_COLORS.get(bill.payment_state, 'grey')
|
color = PAYMENT_STATE_COLORS.get(bill.payment_state, 'grey')
|
||||||
return '<a href="{url}" style="color:{color}" title="{title}">{name}</a>'.format(
|
return '<a href="{url}" style="color:{color}" title="{title}">{name}</a>'.format(
|
||||||
|
|
|
@ -78,7 +78,10 @@ class Domain(models.Model):
|
||||||
@property
|
@property
|
||||||
def is_top(self):
|
def is_top(self):
|
||||||
# don't cache, don't replace by top_id
|
# don't cache, don't replace by top_id
|
||||||
|
try:
|
||||||
return not bool(self.top)
|
return not bool(self.top)
|
||||||
|
except Domain.DoesNotExist:
|
||||||
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def subdomains(self):
|
def subdomains(self):
|
||||||
|
|
|
@ -11,7 +11,7 @@ from .helpers import markdown_formated_changes
|
||||||
from .models import Queue, Ticket
|
from .models import Queue, Ticket
|
||||||
|
|
||||||
|
|
||||||
def change_ticket_state_factory(action, final_state):
|
def change_ticket_state_factory(action, verbose_name, final_state):
|
||||||
context = {
|
context = {
|
||||||
'action': action,
|
'action': action,
|
||||||
'form': ChangeReasonForm()
|
'form': ChangeReasonForm()
|
||||||
|
@ -40,30 +40,31 @@ def change_ticket_state_factory(action, final_state):
|
||||||
'count': queryset.count(),
|
'count': queryset.count(),
|
||||||
'state': final_state.lower()
|
'state': final_state.lower()
|
||||||
}
|
}
|
||||||
msg = _("%s selected tickets are now %s.") % context
|
msg = _("%(count)s selected tickets are now %(state)s.") % context
|
||||||
modeladmin.message_user(request, msg)
|
modeladmin.message_user(request, msg)
|
||||||
else:
|
else:
|
||||||
context['form'] = form
|
context['form'] = form
|
||||||
# action_with_confirmation must display form validation errors
|
# action_with_confirmation must display form validation errors
|
||||||
return True
|
return True
|
||||||
change_ticket_state.url_name = action
|
change_ticket_state.url_name = action
|
||||||
change_ticket_state.verbose_name = action
|
change_ticket_state.tool_description = verbose_name
|
||||||
change_ticket_state.short_description = _('%s selected tickets') % action.capitalize()
|
change_ticket_state.short_description = _('%s selected tickets') % verbose_name
|
||||||
change_ticket_state.help_text = _('Mark ticket as %s.') % final_state.lower()
|
change_ticket_state.help_text = _('Mark ticket as %s.') % final_state.lower()
|
||||||
change_ticket_state.__name__ = action
|
change_ticket_state.__name__ = action
|
||||||
return change_ticket_state
|
return change_ticket_state
|
||||||
|
|
||||||
|
|
||||||
action_map = {
|
action_map = {
|
||||||
Ticket.RESOLVED: 'resolve',
|
Ticket.RESOLVED: ('resolve', _("Resolve")),
|
||||||
Ticket.REJECTED: 'reject',
|
Ticket.REJECTED: ('reject', _("Reject")),
|
||||||
Ticket.CLOSED: 'close'
|
Ticket.CLOSED: ('close', _("Close")),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
thismodule = sys.modules[__name__]
|
thismodule = sys.modules[__name__]
|
||||||
for state, name in action_map.items():
|
for state, names in action_map.items():
|
||||||
action = change_ticket_state_factory(name, state)
|
name, verbose_name = names
|
||||||
|
action = change_ticket_state_factory(name, verbose_name, state)
|
||||||
setattr(thismodule, '%s_tickets' % name, action)
|
setattr(thismodule, '%s_tickets' % name, action)
|
||||||
|
|
||||||
|
|
||||||
|
@ -89,6 +90,7 @@ def take_tickets(modeladmin, request, queryset):
|
||||||
msg = _("%(count)s selected tickets are now owned by %(user)s.") % context
|
msg = _("%(count)s selected tickets are now owned by %(user)s.") % context
|
||||||
modeladmin.message_user(request, msg)
|
modeladmin.message_user(request, msg)
|
||||||
take_tickets.url_name = 'take'
|
take_tickets.url_name = 'take'
|
||||||
|
take_tickets.tool_description = _("Take")
|
||||||
take_tickets.short_description = _("Take selected tickets")
|
take_tickets.short_description = _("Take selected tickets")
|
||||||
take_tickets.help_text = _("Make yourself owner of the ticket.")
|
take_tickets.help_text = _("Make yourself owner of the ticket.")
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,6 @@ class MessageReadOnlyInline(admin.TabularInline):
|
||||||
content_html.short_description = _("Content")
|
content_html.short_description = _("Content")
|
||||||
content_html.allow_tags = True
|
content_html.allow_tags = True
|
||||||
|
|
||||||
|
|
||||||
def has_add_permission(self, request):
|
def has_add_permission(self, request):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -125,11 +124,10 @@ class TicketAdmin(ExtendedModelAdmin):
|
||||||
)
|
)
|
||||||
list_display_links = ('unbold_id', 'bold_subject')
|
list_display_links = ('unbold_id', 'bold_subject')
|
||||||
list_filter = (
|
list_filter = (
|
||||||
MyTicketsListFilter, 'queue__name', 'priority', TicketStateListFilter,
|
MyTicketsListFilter, 'queue', 'priority', TicketStateListFilter,
|
||||||
)
|
)
|
||||||
default_changelist_filters = (
|
default_changelist_filters = (
|
||||||
('my_tickets', lambda r: 'True' if not r.user.is_superuser else 'False'),
|
('state', 'OPEN'),
|
||||||
('state', 'OPEN')
|
|
||||||
)
|
)
|
||||||
date_hierarchy = 'created_at'
|
date_hierarchy = 'created_at'
|
||||||
search_fields = (
|
search_fields = (
|
||||||
|
@ -298,7 +296,7 @@ class QueueAdmin(admin.ModelAdmin):
|
||||||
def num_tickets(self, queue):
|
def num_tickets(self, queue):
|
||||||
num = queue.tickets__count
|
num = queue.tickets__count
|
||||||
url = reverse('admin:issues_ticket_changelist')
|
url = reverse('admin:issues_ticket_changelist')
|
||||||
url += '?my_tickets=False&queue=%i' % queue.pk
|
url += '?queue=%i' % queue.pk
|
||||||
return '<a href="%s">%d</a>' % (url, num)
|
return '<a href="%s">%d</a>' % (url, num)
|
||||||
num_tickets.short_description = _("Tickets")
|
num_tickets.short_description = _("Tickets")
|
||||||
num_tickets.admin_order_field = 'tickets__count'
|
num_tickets.admin_order_field = 'tickets__count'
|
||||||
|
|
|
@ -12,19 +12,12 @@ class MyTicketsListFilter(SimpleListFilter):
|
||||||
def lookups(self, request, model_admin):
|
def lookups(self, request, model_admin):
|
||||||
return (
|
return (
|
||||||
('True', _("My Tickets")),
|
('True', _("My Tickets")),
|
||||||
('False', _("All")),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def queryset(self, request, queryset):
|
def queryset(self, request, queryset):
|
||||||
if self.value() == 'True':
|
if self.value() == 'True':
|
||||||
return queryset.involved_by(request.user)
|
return queryset.involved_by(request.user)
|
||||||
|
|
||||||
def choices(self, cl):
|
|
||||||
""" Remove default All """
|
|
||||||
choices = iter(super(MyTicketsListFilter, self).choices(cl))
|
|
||||||
next(choices)
|
|
||||||
return choices
|
|
||||||
|
|
||||||
|
|
||||||
class TicketStateListFilter(SimpleListFilter):
|
class TicketStateListFilter(SimpleListFilter):
|
||||||
title = 'State'
|
title = 'State'
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import datetime
|
||||||
|
from django.utils.timezone import utc
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('issues', '0002_auto_20150709_1018'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='message',
|
||||||
|
name='created_on',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='message',
|
||||||
|
name='created_at',
|
||||||
|
field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2016, 3, 20, 10, 27, 45, 766388, tzinfo=utc), verbose_name='created at'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,5 +1,6 @@
|
||||||
from django.conf import settings as djsettings
|
from django.conf import settings as djsettings
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
from django.db.models import query, Q
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.contrib.contacts import settings as contacts_settings
|
from orchestra.contrib.contacts import settings as contacts_settings
|
||||||
|
@ -32,6 +33,12 @@ class Queue(models.Model):
|
||||||
super(Queue, self).save(*args, **kwargs)
|
super(Queue, self).save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TicketQuerySet(query.QuerySet):
|
||||||
|
def involved_by(self, user, *args, **kwargs):
|
||||||
|
qset = Q(creator=user) | Q(owner=user) | Q(messages__author=user)
|
||||||
|
return self.filter(qset, *args, **kwargs).distinct()
|
||||||
|
|
||||||
|
|
||||||
class Ticket(models.Model):
|
class Ticket(models.Model):
|
||||||
HIGH = 'HIGH'
|
HIGH = 'HIGH'
|
||||||
MEDIUM = 'MEDIUM'
|
MEDIUM = 'MEDIUM'
|
||||||
|
@ -65,13 +72,13 @@ class Ticket(models.Model):
|
||||||
queue = models.ForeignKey(Queue, related_name='tickets', null=True, blank=True)
|
queue = models.ForeignKey(Queue, related_name='tickets', null=True, blank=True)
|
||||||
subject = models.CharField(_("subject"), max_length=256)
|
subject = models.CharField(_("subject"), max_length=256)
|
||||||
description = models.TextField(_("description"))
|
description = models.TextField(_("description"))
|
||||||
priority = models.CharField(_("priority"), max_length=32, choices=PRIORITIES,
|
priority = models.CharField(_("priority"), max_length=32, choices=PRIORITIES, default=MEDIUM)
|
||||||
default=MEDIUM)
|
|
||||||
state = models.CharField(_("state"), max_length=32, choices=STATES, default=NEW)
|
state = models.CharField(_("state"), max_length=32, choices=STATES, default=NEW)
|
||||||
created_at = models.DateTimeField(_("created"), auto_now_add=True, db_index=True)
|
created_at = models.DateTimeField(_("created"), auto_now_add=True, db_index=True)
|
||||||
updated_at = models.DateTimeField(_("modified"), auto_now=True)
|
updated_at = models.DateTimeField(_("modified"), auto_now=True)
|
||||||
cc = models.TextField("CC", help_text=_("emails to send a carbon copy to"),
|
cc = models.TextField("CC", help_text=_("emails to send a carbon copy to"), blank=True)
|
||||||
blank=True)
|
|
||||||
|
objects = TicketQuerySet.as_manager()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['-updated_at']
|
ordering = ['-updated_at']
|
||||||
|
@ -158,7 +165,7 @@ class Message(models.Model):
|
||||||
related_name='ticket_messages')
|
related_name='ticket_messages')
|
||||||
author_name = models.CharField(_("author name"), max_length=256, blank=True)
|
author_name = models.CharField(_("author name"), max_length=256, blank=True)
|
||||||
content = models.TextField(_("content"))
|
content = models.TextField(_("content"))
|
||||||
created_on = models.DateTimeField(_("created on"), auto_now_add=True)
|
created_at = models.DateTimeField(_("created at"), auto_now_add=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
get_latest_by = 'id'
|
get_latest_by = 'id'
|
||||||
|
|
|
@ -13,8 +13,8 @@ class QueueSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
class MessageSerializer(serializers.HyperlinkedModelSerializer):
|
class MessageSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Message
|
model = Message
|
||||||
fields = ('id', 'author', 'author_name', 'content', 'created_on')
|
fields = ('id', 'author', 'author_name', 'content', 'created_at')
|
||||||
read_only_fields = ('author', 'author_name', 'created_on')
|
read_only_fields = ('author', 'author_name', 'created_at')
|
||||||
|
|
||||||
def get_identity(self, data):
|
def get_identity(self, data):
|
||||||
return data.get('id')
|
return data.get('id')
|
||||||
|
|
|
@ -18,7 +18,7 @@ Issue #{{ ticket.id }} has been updated by {{ ticket_message.author }}.
|
||||||
-----------------------------------------------------------------
|
-----------------------------------------------------------------
|
||||||
Issue #{{ ticket.pk }}: {{ ticket.subject }}
|
Issue #{{ ticket.pk }}: {{ ticket.subject }}
|
||||||
|
|
||||||
* Author: {{ ticket.created_by }}
|
* Author: {{ ticket.creator_name }}
|
||||||
* Status: {{ ticket.get_state_display }}
|
* Status: {{ ticket.get_state_display }}
|
||||||
* Priority: {{ ticket.get_priority_display }}
|
* Priority: {{ ticket.get_priority_display }}
|
||||||
* Visibility: {{ ticket.get_visibility_display }}
|
* Visibility: {{ ticket.get_visibility_display }}
|
||||||
|
|
|
@ -41,7 +41,7 @@ Issue #{{ ticket.id }} has been updated by {{ ticket_message.author }}.
|
||||||
<h1><a href="http://{{ site.domain }}{% url 'admin:issues_ticket_change' ticket.id %}">Issue #{{ ticket.pk }}: {{ ticket.subject }}</a></h1>
|
<h1><a href="http://{{ site.domain }}{% url 'admin:issues_ticket_change' ticket.id %}">Issue #{{ ticket.pk }}: {{ ticket.subject }}</a></h1>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Author: {{ ticket.created_by }}</li>
|
<li>Author: {{ ticket.creator_name }}</li>
|
||||||
<li>Status: {{ ticket.get_state_display }}</li>
|
<li>Status: {{ ticket.get_state_display }}</li>
|
||||||
<li>Priority: {{ ticket.get_priority_display }}</li>
|
<li>Priority: {{ ticket.get_priority_display }}</li>
|
||||||
<li>Visibility: {{ ticket.get_visibility_display }}</li>
|
<li>Visibility: {{ ticket.get_visibility_display }}</li>
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.contrib.auth.admin import UserAdmin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
||||||
from orchestra.admin.actions import disable
|
from orchestra.admin.actions import disable, enable
|
||||||
from orchestra.admin.utils import admin_link
|
from orchestra.admin.utils import admin_link
|
||||||
from orchestra.contrib.accounts.actions import list_accounts
|
from orchestra.contrib.accounts.actions import list_accounts
|
||||||
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
|
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
|
||||||
|
@ -58,7 +58,7 @@ class ListAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedModel
|
||||||
add_form = ListCreationForm
|
add_form = ListCreationForm
|
||||||
list_select_related = ('account', 'address_domain',)
|
list_select_related = ('account', 'address_domain',)
|
||||||
filter_by_account_fields = ['address_domain']
|
filter_by_account_fields = ['address_domain']
|
||||||
actions = (disable, list_accounts)
|
actions = (disable, enable, list_accounts)
|
||||||
|
|
||||||
address_domain_link = admin_link('address_domain', order='address_domain__name')
|
address_domain_link = admin_link('address_domain', order='address_domain__name')
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
||||||
from orchestra.admin.actions import disable
|
from orchestra.admin.actions import disable, enable
|
||||||
from orchestra.admin.utils import admin_link, change_url
|
from orchestra.admin.utils import admin_link, change_url
|
||||||
from orchestra.contrib.accounts.actions import list_accounts
|
from orchestra.contrib.accounts.actions import list_accounts
|
||||||
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
|
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
|
||||||
|
@ -74,7 +74,7 @@ class MailboxAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, ExtendedMo
|
||||||
add_form = MailboxCreationForm
|
add_form = MailboxCreationForm
|
||||||
form = MailboxChangeForm
|
form = MailboxChangeForm
|
||||||
list_prefetch_related = ('addresses__domain',)
|
list_prefetch_related = ('addresses__domain',)
|
||||||
actions = (disable, list_accounts)
|
actions = (disable, enable, list_accounts)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(MailboxAdmin, self).__init__(*args, **kwargs)
|
super(MailboxAdmin, self).__init__(*args, **kwargs)
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
||||||
from orchestra.admin.actions import disable
|
from orchestra.admin.actions import disable, enable
|
||||||
from orchestra.admin.utils import change_url
|
from orchestra.admin.utils import change_url
|
||||||
from orchestra.contrib.accounts.actions import list_accounts
|
from orchestra.contrib.accounts.actions import list_accounts
|
||||||
from orchestra.contrib.accounts.admin import AccountAdminMixin
|
from orchestra.contrib.accounts.admin import AccountAdminMixin
|
||||||
|
@ -24,7 +24,7 @@ class SaaSAdmin(SelectPluginAdminMixin, ChangePasswordAdminMixin, AccountAdminMi
|
||||||
plugin = SoftwareService
|
plugin = SoftwareService
|
||||||
plugin_field = 'service'
|
plugin_field = 'service'
|
||||||
plugin_title = 'Software as a Service'
|
plugin_title = 'Software as a Service'
|
||||||
actions = (disable, list_accounts)
|
actions = (disable, enable, list_accounts)
|
||||||
|
|
||||||
def display_url(self, saas):
|
def display_url(self, saas):
|
||||||
site_domain = saas.get_site_domain()
|
site_domain = saas.get_site_domain()
|
||||||
|
|
|
@ -21,6 +21,7 @@ class DrupalMuController(ServiceController):
|
||||||
|
|
||||||
def save(self, webapp):
|
def save(self, webapp):
|
||||||
context = self.get_context(webapp)
|
context = self.get_context(webapp)
|
||||||
|
# TODO set password
|
||||||
self.append(textwrap.dedent("""\
|
self.append(textwrap.dedent("""\
|
||||||
mkdir %(drupal_path)s
|
mkdir %(drupal_path)s
|
||||||
chown -R www-data %(drupal_path)s
|
chown -R www-data %(drupal_path)s
|
||||||
|
@ -35,6 +36,7 @@ class DrupalMuController(ServiceController):
|
||||||
|
|
||||||
def delete(self, webapp):
|
def delete(self, webapp):
|
||||||
context = self.get_context(webapp)
|
context = self.get_context(webapp)
|
||||||
|
# TODO delete tables
|
||||||
self.append("rm -fr %(app_path)s" % context)
|
self.append("rm -fr %(app_path)s" % context)
|
||||||
|
|
||||||
def get_context(self, webapp):
|
def get_context(self, webapp):
|
||||||
|
|
|
@ -22,8 +22,9 @@ class WordpressMuController(ServiceController):
|
||||||
model = 'saas.SaaS'
|
model = 'saas.SaaS'
|
||||||
default_route_match = "saas.service == 'wordpress'"
|
default_route_match = "saas.service == 'wordpress'"
|
||||||
doc_settings = (settings,
|
doc_settings = (settings,
|
||||||
('SAAS_WORDPRESS_ADMIN_PASSWORD', 'SAAS_WORDPRESS_MAIN_URL')
|
('SAAS_WORDPRESS_ADMIN_PASSWORD', 'SAAS_WORDPRESS_MAIN_URL', 'SAAS_WORDPRESS_VERIFY_SSL')
|
||||||
)
|
)
|
||||||
|
VERIFY = settings.SAAS_WORDPRESS_VERIFY_SSL
|
||||||
|
|
||||||
def login(self, session):
|
def login(self, session):
|
||||||
main_url = self.get_main_url()
|
main_url = self.get_main_url()
|
||||||
|
@ -33,9 +34,10 @@ class WordpressMuController(ServiceController):
|
||||||
'pwd': settings.SAAS_WORDPRESS_ADMIN_PASSWORD,
|
'pwd': settings.SAAS_WORDPRESS_ADMIN_PASSWORD,
|
||||||
'redirect_to': '/wp-admin/'
|
'redirect_to': '/wp-admin/'
|
||||||
}
|
}
|
||||||
response = session.post(login_url, data=login_data)
|
sys.stdout.write("Login URL: %s\n" % login_url)
|
||||||
|
response = session.post(login_url, data=login_data, verify=self.VERIFY)
|
||||||
if response.url != main_url + '/wp-admin/':
|
if response.url != main_url + '/wp-admin/':
|
||||||
raise IOError("Failure login to remote application")
|
raise IOError("Failure login to remote application (%s)" % login_url)
|
||||||
|
|
||||||
def get_main_url(self):
|
def get_main_url(self):
|
||||||
main_url = settings.SAAS_WORDPRESS_MAIN_URL
|
main_url = settings.SAAS_WORDPRESS_MAIN_URL
|
||||||
|
@ -54,7 +56,8 @@ class WordpressMuController(ServiceController):
|
||||||
'<a href="http://[\.\-\w]+/wp-admin/network/site-info\.php\?id=([0-9]+)"\s+'
|
'<a href="http://[\.\-\w]+/wp-admin/network/site-info\.php\?id=([0-9]+)"\s+'
|
||||||
'class="edit">%s</a>' % saas.name
|
'class="edit">%s</a>' % saas.name
|
||||||
)
|
)
|
||||||
content = session.get(search).content.decode('utf8')
|
sys.stdout.write("Search URL: %s\n" % search)
|
||||||
|
content = session.get(search, verify=self.VERIFY).content.decode('utf8')
|
||||||
# Get id
|
# Get id
|
||||||
ids = regex.search(content)
|
ids = regex.search(content)
|
||||||
if not ids and not blog_id:
|
if not ids and not blog_id:
|
||||||
|
@ -85,7 +88,8 @@ class WordpressMuController(ServiceController):
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
url = self.get_main_url()
|
url = self.get_main_url()
|
||||||
url += '/wp-admin/network/site-new.php'
|
url += '/wp-admin/network/site-new.php'
|
||||||
content = session.get(url).content.decode('utf8')
|
sys.stdout.write("Create URL: %s\n" % url)
|
||||||
|
content = session.get(url, verify=self.VERIFY).content.decode('utf8')
|
||||||
|
|
||||||
wpnonce = re.compile('name="_wpnonce_add-blog"\s+value="([^"]*)"')
|
wpnonce = re.compile('name="_wpnonce_add-blog"\s+value="([^"]*)"')
|
||||||
wpnonce = wpnonce.search(content).groups()[0]
|
wpnonce = wpnonce.search(content).groups()[0]
|
||||||
|
@ -99,7 +103,7 @@ class WordpressMuController(ServiceController):
|
||||||
}
|
}
|
||||||
|
|
||||||
# Validate response
|
# Validate response
|
||||||
response = session.post(url, data=data)
|
response = session.post(url, data=data, verify=self.VERIFY)
|
||||||
self.validate_response(response)
|
self.validate_response(response)
|
||||||
blog_id = re.compile(r'<link id="wp-admin-canonical" rel="canonical" href="http(?:[^ ]+)/wp-admin/network/site-new.php\?id=([0-9]+)" />')
|
blog_id = re.compile(r'<link id="wp-admin-canonical" rel="canonical" href="http(?:[^ ]+)/wp-admin/network/site-new.php\?id=([0-9]+)" />')
|
||||||
content = response.content.decode('utf8')
|
content = response.content.decode('utf8')
|
||||||
|
@ -124,8 +128,9 @@ class WordpressMuController(ServiceController):
|
||||||
delete = self.get_main_url()
|
delete = self.get_main_url()
|
||||||
delete += '/wp-admin/network/sites.php?action=confirm&action2=deleteblog'
|
delete += '/wp-admin/network/sites.php?action=confirm&action2=deleteblog'
|
||||||
delete += '&id=%d&_wpnonce=%s' % (id, wpnonce)
|
delete += '&id=%d&_wpnonce=%s' % (id, wpnonce)
|
||||||
|
sys.stdout.write("Search URL: %s\n" % delete)
|
||||||
|
|
||||||
content = session.get(delete).content.decode('utf8')
|
content = session.get(delete, verify=self.VERIFY).content.decode('utf8')
|
||||||
wpnonce = re.compile('name="_wpnonce"\s+value="([^"]*)"')
|
wpnonce = re.compile('name="_wpnonce"\s+value="([^"]*)"')
|
||||||
wpnonce = wpnonce.search(content).groups()[0]
|
wpnonce = wpnonce.search(content).groups()[0]
|
||||||
data = {
|
data = {
|
||||||
|
@ -136,7 +141,8 @@ class WordpressMuController(ServiceController):
|
||||||
}
|
}
|
||||||
delete = self.get_main_url()
|
delete = self.get_main_url()
|
||||||
delete += '/wp-admin/network/sites.php?action=deleteblog'
|
delete += '/wp-admin/network/sites.php?action=deleteblog'
|
||||||
response = session.post(delete, data=data)
|
sys.stdout.write("Delete URL: %s\n" % delete)
|
||||||
|
response = session.post(delete, data=data, verify=self.VERIFY)
|
||||||
self.validate_response(response)
|
self.validate_response(response)
|
||||||
|
|
||||||
def save(self, saas):
|
def save(self, saas):
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
from .options import SoftwareService
|
from .options import SoftwareService
|
||||||
|
|
||||||
|
from .. import settings
|
||||||
|
|
||||||
|
|
||||||
class DrupalService(SoftwareService):
|
class DrupalService(SoftwareService):
|
||||||
name = 'drupal'
|
name = 'drupal'
|
||||||
verbose_name = "Drupal"
|
verbose_name = "Drupal"
|
||||||
icon = 'orchestra/icons/apps/Drupal.png'
|
icon = 'orchestra/icons/apps/Drupal.png'
|
||||||
|
site_domain = settings.SAAS_MOODLE_DOMAIN
|
||||||
|
|
|
@ -65,6 +65,11 @@ SAAS_WORDPRESS_DB_NAME = Setting('SAAS_WORDPRESS_DB_NAME',
|
||||||
help_text=_("Needed for domain mapping when <tt>SAAS_WORDPRESS_ALLOW_CUSTOM_URL</tt> is enabled."),
|
help_text=_("Needed for domain mapping when <tt>SAAS_WORDPRESS_ALLOW_CUSTOM_URL</tt> is enabled."),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SAAS_WORDPRESS_VERIFY_SSL = Setting('SAAS_WORDPRESS_VERIFY_SSL',
|
||||||
|
True,
|
||||||
|
help_text=_("Verify SSL certificate on the HTTP requests performed by the backend."),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# DokuWiki
|
# DokuWiki
|
||||||
|
|
||||||
|
@ -119,6 +124,11 @@ SAAS_DRUPAL_SITES_PATH = Setting('WEBSITES_DRUPAL_SITES_PATH',
|
||||||
'/home/httpd/htdocs/drupal-mu/sites/%(site_name)s',
|
'/home/httpd/htdocs/drupal-mu/sites/%(site_name)s',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SAAS_DRUPAL_DOMAIN = Setting('SAAS_DRUPAL_DOMAIN',
|
||||||
|
'%(site_name)s.drupal.{}'.format(ORCHESTRA_BASE_DOMAIN),
|
||||||
|
help_text="Uses <tt>ORCHESTRA_BASE_DOMAIN</tt> by default.",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# PhpList
|
# PhpList
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ from django.contrib import admin
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
from orchestra.admin import ExtendedModelAdmin, ChangePasswordAdminMixin
|
||||||
|
from orchestra.admin.actions import disable, enable
|
||||||
from orchestra.contrib.accounts.actions import list_accounts
|
from orchestra.contrib.accounts.actions import list_accounts
|
||||||
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
|
from orchestra.contrib.accounts.admin import SelectAccountAdminMixin
|
||||||
from orchestra.contrib.accounts.filters import IsActiveListFilter
|
from orchestra.contrib.accounts.filters import IsActiveListFilter
|
||||||
|
@ -42,7 +43,7 @@ class SystemUserAdmin(ChangePasswordAdminMixin, SelectAccountAdminMixin, Extende
|
||||||
form = SystemUserChangeForm
|
form = SystemUserChangeForm
|
||||||
ordering = ('-id',)
|
ordering = ('-id',)
|
||||||
change_view_actions = (set_permission, create_link)
|
change_view_actions = (set_permission, create_link)
|
||||||
actions = (delete_selected, list_accounts) + change_view_actions
|
actions = (disable, enable, delete_selected, list_accounts) + change_view_actions
|
||||||
|
|
||||||
def display_main(self, user):
|
def display_main(self, user):
|
||||||
return user.is_main
|
return user.is_main
|
||||||
|
|
|
@ -6,7 +6,7 @@ from django.utils.encoding import force_text
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from orchestra.admin import ExtendedModelAdmin
|
from orchestra.admin import ExtendedModelAdmin
|
||||||
from orchestra.admin.actions import disable
|
from orchestra.admin.actions import disable, enable
|
||||||
from orchestra.admin.utils import admin_link, change_url
|
from orchestra.admin.utils import admin_link, change_url
|
||||||
from orchestra.contrib.accounts.actions import list_accounts
|
from orchestra.contrib.accounts.actions import list_accounts
|
||||||
from orchestra.contrib.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
|
from orchestra.contrib.accounts.admin import AccountAdminMixin, SelectAccountAdminMixin
|
||||||
|
@ -76,7 +76,7 @@ class WebsiteAdmin(SelectAccountAdminMixin, ExtendedModelAdmin):
|
||||||
filter_by_account_fields = ['domains']
|
filter_by_account_fields = ['domains']
|
||||||
list_prefetch_related = ('domains', 'content_set__webapp')
|
list_prefetch_related = ('domains', 'content_set__webapp')
|
||||||
search_fields = ('name', 'account__username', 'domains__name', 'content__webapp__name')
|
search_fields = ('name', 'account__username', 'domains__name', 'content__webapp__name')
|
||||||
actions = (disable, list_accounts)
|
actions = (disable, enable, list_accounts)
|
||||||
|
|
||||||
def display_domains(self, website):
|
def display_domains(self, website):
|
||||||
domains = []
|
domains = []
|
||||||
|
|
Loading…
Reference in New Issue