Compare commits

..

4 Commits

5 changed files with 60 additions and 58 deletions

View File

@ -1,12 +1,13 @@
from functools import partial
from django.contrib import admin
from django.core.exceptions import PermissionDenied
from django.core.mail import send_mass_mail
from django.shortcuts import render
from django.utils.translation import ngettext, gettext_lazy as _
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ngettext
from .. import settings
from .decorators import action_with_confirmation
from .forms import SendEmailForm
@ -18,7 +19,7 @@ class SendEmail(object):
template = 'admin/orchestra/generic_confirmation.html'
default_from = settings.ORCHESTRA_DEFAULT_SUPPORT_FROM_EMAIL
__name__ = 'semd_email'
def __call__(self, modeladmin, request, queryset):
""" make this monster behave like a function """
self.modeladmin = modeladmin
@ -34,10 +35,10 @@ class SendEmail(object):
'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
}
return self.write_email(request)
def write_email(self, request):
if not request.user.is_superuser:
raise PermissionDenied
raise PermissionDenied()
initial={
'email_from': self.default_from,
'to': ' '.join(self.get_email_addresses())
@ -51,7 +52,7 @@ class SendEmail(object):
'extra_to': form.cleaned_data['extra_to'],
'subject': form.cleaned_data['subject'],
'message': form.cleaned_data['message'],
}
return self.confirm_email(request, **options)
self.context.update({
@ -62,10 +63,10 @@ class SendEmail(object):
})
# Display confirmation page
return render(request, self.template, self.context)
def get_email_addresses(self):
return self.queryset.values_list('email', flat=True)
def confirm_email(self, request, **options):
email_from = options['email_from']
extra_to = options['extra_to']
@ -88,7 +89,7 @@ class SendEmail(object):
)
self.modeladmin.message_user(request, msg)
return None
form = self.form(initial={
'email_from': email_from,
'extra_to': ', '.join(extra_to),

View File

@ -1,16 +1,16 @@
from urllib import parse
from django import forms
from django.urls import re_path as url
from django.contrib import admin, messages
from django.contrib.admin.options import IS_POPUP_VAR
from django.contrib.admin.utils import unquote
from django.contrib.auth import update_session_auth_hash
from django.core.exceptions import PermissionDenied
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.forms.models import BaseInlineFormSet
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.template.response import TemplateResponse
from django.urls import re_path as url
from django.utils.decorators import method_decorator
from django.utils.encoding import force_str
from django.utils.html import escape
@ -19,14 +19,12 @@ from django.views.decorators.debug import sensitive_post_parameters
from orchestra.models.utils import has_db_field
from ..utils.python import random_ascii, pairwise
from ..utils.python import pairwise, random_ascii
from .forms import AdminPasswordChangeForm
#, AdminRawPasswordChangeForm
#from django.contrib.auth.forms import AdminPasswordChangeForm
from .utils import action_to_view
sensitive_post_parameters_m = method_decorator(sensitive_post_parameters())
@ -37,7 +35,7 @@ class ChangeListDefaultFilter(object):
default_changelist_filters = (('my_nodes', 'True'),)
"""
default_changelist_filters = ()
def changelist_view(self, request, extra_context=None):
# defaults = []
# for key, value in self.default_changelist_filters:
@ -79,7 +77,7 @@ class EnhaceSearchMixin(object):
if 'password' in lookup:
return False
return True
def get_search_results(self, request, queryset, search_term):
""" allows to specify field <field_name>:<search_term> """
search_fields = self.get_search_fields(request)
@ -109,7 +107,7 @@ class ChangeViewActionsMixin(object):
""" Makes actions visible on the admin change view page. """
change_view_actions = ()
change_form_template = 'orchestra/admin/change_form.html'
def get_urls(self):
"""Returns the additional urls for the change view links"""
urls = super(ChangeViewActionsMixin, self).get_urls()
@ -124,7 +122,7 @@ class ChangeViewActionsMixin(object):
)
)
return new_urls + urls
def get_change_view_actions(self, obj=None):
""" allow customization on modelamdin """
views = []
@ -145,7 +143,7 @@ class ChangeViewActionsMixin(object):
view.hidden = getattr(action, 'hidden', False)
views.append(view)
return views
def change_view(self, request, object_id, **kwargs):
if kwargs.get('extra_context', None) is None:
kwargs['extra_context'] = {}
@ -165,21 +163,21 @@ class ChangeAddFieldsMixin(object):
change_readonly_fields = ()
change_form = None
add_inlines = None
def get_prepopulated_fields(self, request, obj=None):
if not obj:
return super(ChangeAddFieldsMixin, self).get_prepopulated_fields(request, obj)
return {}
def get_change_readonly_fields(self, request, obj=None):
return self.change_readonly_fields
def get_readonly_fields(self, request, obj=None):
fields = super(ChangeAddFieldsMixin, self).get_readonly_fields(request, obj)
if obj:
return fields + self.get_change_readonly_fields(request, obj)
return fields
def get_fieldsets(self, request, obj=None):
if not obj:
if self.add_fieldsets:
@ -187,7 +185,7 @@ class ChangeAddFieldsMixin(object):
elif self.add_fields:
return [(None, {'fields': self.add_fields})]
return super(ChangeAddFieldsMixin, self).get_fieldsets(request, obj)
def get_inline_instances(self, request, obj=None):
""" add_inlines and inline.parent_object """
if obj:
@ -198,7 +196,7 @@ class ChangeAddFieldsMixin(object):
for inline in inlines:
inline.parent_object = obj
return inlines
def get_form(self, request, obj=None, **kwargs):
""" Use special form during user creation """
defaults = {}
@ -218,13 +216,13 @@ class ExtendedModelAdmin(ChangeViewActionsMixin,
EnhaceSearchMixin,
admin.ModelAdmin):
list_prefetch_related = None
def get_queryset(self, request):
qs = super(ExtendedModelAdmin, self).get_queryset(request)
if self.list_prefetch_related:
qs = qs.prefetch_related(*self.list_prefetch_related)
return qs
def get_object(self, request, object_id, from_field=None):
obj = super(ExtendedModelAdmin, self).get_object(request, object_id, from_field)
if obj is None:
@ -237,7 +235,7 @@ class ExtendedModelAdmin(ChangeViewActionsMixin,
class ChangePasswordAdminMixin(object):
change_password_form = AdminPasswordChangeForm
change_user_password_template = 'admin/orchestra/change_password.html'
def get_urls(self):
opts = self.model._meta
info = opts.app_label, opts.model_name
@ -249,14 +247,14 @@ class ChangePasswordAdminMixin(object):
self.admin_site.admin_view(self.show_hash),
name='%s_%s_show_hash' % info)
] + super().get_urls()
def get_change_password_username(self, obj):
return str(obj)
@sensitive_post_parameters_m
def change_password(self, request, id, form_url=''):
if not self.has_change_permission(request):
raise PermissionDenied
raise PermissionDenied()
# TODO use this insetad of self.get_object(), in other places
obj = get_object_or_404(self.get_queryset(request), pk=id)
raw = request.GET.get('raw', '0') == '1'
@ -281,7 +279,7 @@ class ChangePasswordAdminMixin(object):
for rel in account.get_related_passwords(db_field=raw):
if not isinstance(obj, type(rel)):
related.append(rel)
if request.method == 'POST':
form = self.change_password_form(obj, request.POST, related=related, raw=raw)
if form.is_valid():
@ -293,7 +291,7 @@ class ChangePasswordAdminMixin(object):
return HttpResponseRedirect('..')
else:
form = self.change_password_form(obj, related=related, raw=raw)
fieldsets = [
(obj._meta.verbose_name.capitalize(), {
'classes': ('wide',),
@ -305,7 +303,7 @@ class ChangePasswordAdminMixin(object):
'classes': ('wide',),
'fields': ('password_%i' % ix,) if raw else ('password1_%i' % ix, 'password2_%i' % ix)
}))
obj_username = self.get_change_password_username(obj)
adminForm = admin.helpers.AdminForm(form, fieldsets, {})
context = {
@ -331,9 +329,9 @@ class ChangePasswordAdminMixin(object):
}
context.update(admin.site.each_context(request))
return TemplateResponse(request, self.change_user_password_template, context)
def show_hash(self, request, id):
if not request.user.is_superuser:
raise PermissionDenied
raise PermissionDenied()
obj = get_object_or_404(self.get_queryset(request), pk=id)
return HttpResponse(obj.password)

View File

@ -143,6 +143,8 @@ DATABASES = {
}
}
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
# Password validation
# https://docs.djangoproject.com/en/{{ docs_version }}/ref/settings/#auth-password-validators
@ -233,6 +235,7 @@ FLUENT_DASHBOARD_ICON_THEME = '../orchestra/icons'
# Django-celery
import djcelery
djcelery.setup_loader()
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'

View File

@ -1,14 +1,14 @@
import sys
from contextlib import ContextDecorator
from threading import local
from django.contrib.admin.models import LogEntry
from django.db.models.signals import pre_delete, post_save, m2m_changed
from django.db.models.signals import m2m_changed, post_save, pre_delete
from django.dispatch import receiver
from django.utils.decorators import ContextDecorator
from orchestra.utils.python import OrderedSet
from . import manager, Operation, helpers
from . import Operation, helpers, manager
from .middlewares import OperationsMiddleware
from .models import BackendLog, BackendOperation
@ -37,7 +37,7 @@ def m2m_collector(sender, *args, **kwargs):
class orchestrate(ContextDecorator):
"""
Context manager for triggering backend operations out of request-response cycle, e.g. shell
with orchestrate():
user = SystemUser.objects.get(username='rata')
user.shell = '/dev/null'
@ -46,7 +46,7 @@ class orchestrate(ContextDecorator):
thread_locals = local()
thread_locals.pending_operations = None
thread_locals.route_cache = None
@classmethod
def collect(cls, action, **kwargs):
""" Collects all pending operations derived from model signals """
@ -57,14 +57,14 @@ class orchestrate(ContextDecorator):
kwargs['route_cache'] = cls.thread_locals.route_cache
instance = kwargs.pop('instance')
manager.collect(instance, action, **kwargs)
def __enter__(self):
cls = type(self)
self.old_pending_operations = cls.thread_locals.pending_operations
cls.thread_locals.pending_operations = OrderedSet()
self.old_route_cache = cls.thread_locals.route_cache
cls.thread_locals.route_cache = {}
def __exit__(self, exc_type, exc_value, traceback):
cls = type(self)
if not exc_type:

View File

@ -1,23 +1,23 @@
Django==2.2.24
django-fluent-dashboard==1.0.1
django-admin-tools==0.9.1
django-extensions==3.1.3
django-celery==3.2.1
celery==3.1.23
kombu==3.0.35
Django==3.2.23
django-fluent-dashboard==2.0
django-admin-tools==0.9.3
django-extensions==3.2.3
django-celery==3.3.1
celery<4.0,>=3.1.15
kombu==3.0.37
billiard==3.3.0.23
Markdown==3.3.4
djangorestframework==3.12.4
Pygments==2.9.0
django-filter==2.4.0
Markdown==3.5.1
djangorestframework==3.14.0
Pygments==2.17.2
django-filter==23.4
jsonfield==3.1.0
python-dateutil>=2.7.0
python-dateutil==2.8.2
passlib==1.7.4
django-iban==0.3.0
django-iban==0.3.1
requests
phonenumbers==8.12.27
phonenumbers==8.13.26
django-countries
django-localflavor==3.1
django-localflavor==4.0
amqp
anyjson
pytz