Compare commits
No commits in common. "master" and "0.8.7" have entirely different histories.
|
@ -3,4 +3,3 @@
|
|||
/django_gas.egg-info
|
||||
/.coverage
|
||||
/htmlcov
|
||||
*.pyc
|
||||
|
|
32
changelog.md
32
changelog.md
|
@ -1,38 +1,6 @@
|
|||
Changelog
|
||||
=========
|
||||
|
||||
0.8.13
|
||||
------
|
||||
|
||||
* Fix: set html email template
|
||||
|
||||
0.8.12
|
||||
------
|
||||
|
||||
* Add reset password (thanks Leo!)
|
||||
|
||||
0.8.11
|
||||
------
|
||||
|
||||
* Add preview widget in Image Field Form (thanks Leo!)
|
||||
|
||||
0.8.10
|
||||
-----
|
||||
|
||||
* New templatetag to check if user has role
|
||||
|
||||
0.8.9
|
||||
-----
|
||||
|
||||
* Bugfix
|
||||
|
||||
0.8.8
|
||||
-----
|
||||
|
||||
* Support Django 4.2
|
||||
* Use DeleteView.form_valid instead of DeleteView.delete
|
||||
* Add no-cache headers to login redirects
|
||||
|
||||
0.8.7
|
||||
-----
|
||||
|
||||
|
|
|
@ -2,4 +2,3 @@ Collaborators
|
|||
=============
|
||||
|
||||
* Shakarina
|
||||
* Leo Hakim
|
||||
|
|
|
@ -8,9 +8,5 @@ urlpatterns = [
|
|||
path('login/', views.GASLoginView.as_view(), name='login'),
|
||||
path('logout/', logout_then_login, {'login_url': 'gas:login'}, name='logout'),
|
||||
path('change-password/', views.GASPasswordChangeView.as_view(), name='change_password'),
|
||||
path('reset-password-confirm/<uidb64>/<token>/', views.GASPasswordResetConfirmView.as_view(), name='password_reset_confirm'),
|
||||
path('reset-password-confirm/done/', views.GASPasswordResetCompleteView.as_view(), name='password_reset_complete'),
|
||||
path('reset-password/done/', views.GASPasswordResetDoneView.as_view(), name='password_reset_done'),
|
||||
path('reset-password/', views.GASPasswordResetView.as_view(), name='reset_password'),
|
||||
path('', views.Index.as_view(), name='index'),
|
||||
]
|
||||
|
|
|
@ -1,72 +1,45 @@
|
|||
from django.contrib.auth.views import (
|
||||
LoginView,
|
||||
PasswordChangeView,
|
||||
PasswordResetConfirmView,
|
||||
PasswordResetDoneView,
|
||||
PasswordResetView,
|
||||
)
|
||||
from django.contrib.auth.views import LoginView, PasswordChangeView
|
||||
from django.shortcuts import resolve_url
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.generic import TemplateView
|
||||
from gas import gas_settings
|
||||
|
||||
from gas.views import GASMixin
|
||||
|
||||
|
||||
class GASPasswordChangeView(GASMixin, PasswordChangeView):
|
||||
template_name = "gas/base_form.html"
|
||||
success_url = reverse_lazy("gas:index")
|
||||
continue_url = reverse_lazy("gas:change_password")
|
||||
title = _("Change your password")
|
||||
success_message = _("Password changed.")
|
||||
from gas import gas_settings
|
||||
|
||||
|
||||
class Index(GASMixin, TemplateView):
|
||||
main_menu = "index"
|
||||
template_name = "gas/index.html"
|
||||
roles = ("staff",)
|
||||
|
||||
|
||||
class GASContextMixin:
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
css = gas_settings.MEDIA["css"]
|
||||
javascript = gas_settings.MEDIA["js"]
|
||||
if gas_settings.EXTRA_MEDIA:
|
||||
css = css + gas_settings.EXTRA_MEDIA.get("css", [])
|
||||
javascript = javascript + gas_settings.EXTRA_MEDIA.get("js", [])
|
||||
ctx.update(
|
||||
{
|
||||
"logo_static_url": gas_settings.LOGO,
|
||||
"css": css,
|
||||
"js": javascript,
|
||||
}
|
||||
)
|
||||
return ctx
|
||||
|
||||
|
||||
class GASLoginView(GASContextMixin, LoginView):
|
||||
class GASLoginView(LoginView):
|
||||
template_name = "gas/login.html"
|
||||
|
||||
def get_success_url(self):
|
||||
url = self.get_redirect_url()
|
||||
return url or resolve_url("gas:index")
|
||||
return url or resolve_url('gas:index')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
css = gas_settings.MEDIA['css']
|
||||
js = gas_settings.MEDIA['js']
|
||||
if gas_settings.EXTRA_MEDIA:
|
||||
css = css + gas_settings.EXTRA_MEDIA.get('css', [])
|
||||
js = js + gas_settings.EXTRA_MEDIA.get('js', [])
|
||||
ctx.update({
|
||||
'logo_static_url': gas_settings.LOGO,
|
||||
'css': css,
|
||||
'js': js,
|
||||
})
|
||||
return ctx
|
||||
|
||||
|
||||
class GASPasswordResetView(GASContextMixin, PasswordResetView):
|
||||
template_name = "gas/reset.html"
|
||||
html_email_template_name = "registration/password_reset_email.html"
|
||||
success_url = reverse_lazy("gas:password_reset_done")
|
||||
class GASPasswordChangeView(GASMixin, PasswordChangeView):
|
||||
template_name = 'gas/base_form.html'
|
||||
success_url = reverse_lazy('gas:index')
|
||||
continue_url = reverse_lazy('gas:change_password')
|
||||
title = _('Change your password')
|
||||
success_message = _('Password changed.')
|
||||
|
||||
|
||||
class GASPasswordResetDoneView(GASContextMixin, PasswordResetDoneView):
|
||||
template_name = "gas/reset_done.html"
|
||||
|
||||
|
||||
class GASPasswordResetConfirmView(GASContextMixin, PasswordResetConfirmView):
|
||||
template_name = "gas/reset_confirm.html"
|
||||
success_url = reverse_lazy("gas:password_reset_complete")
|
||||
|
||||
|
||||
class GASPasswordResetCompleteView(GASContextMixin, TemplateView):
|
||||
template_name = "gas/reset_complete.html"
|
||||
class Index(GASMixin, TemplateView):
|
||||
main_menu = 'index'
|
||||
template_name = "gas/index.html"
|
||||
roles = ('staff',)
|
||||
|
|
|
@ -7,9 +7,6 @@ urlpatterns = [
|
|||
path('', views.UserList.as_view(), name="user_list"),
|
||||
path('create/', views.CreateUser.as_view(), name="user_create"),
|
||||
path('<int:pk>/edit/', views.UpdateUser.as_view(), name="user_update"),
|
||||
path(
|
||||
'<int:pk>/change-password/',
|
||||
views.ChangePasswordUser.as_view(),
|
||||
name="user_change_password"),
|
||||
path('<int:pk>/change-password/', views.ChangePasswordUser.as_view(), name="user_change_password"),
|
||||
path('<int:pk>/delete/', views.DeleteUser.as_view(), name="user_delete"),
|
||||
]
|
||||
|
|
|
@ -20,5 +20,3 @@ MEDIA = getattr(settings, 'GAS_MEDIA', {
|
|||
})
|
||||
|
||||
EXTRA_MEDIA = getattr(settings, 'GAS_EXTRA_MEDIA', None)
|
||||
|
||||
IMAGE_PREVIEW_WIDTH = getattr(settings, 'IMAGE_PREVIEW_WIDTH', 240)
|
||||
|
|
14
gas/sites.py
14
gas/sites.py
|
@ -10,7 +10,7 @@ class Entry:
|
|||
self.icon = icon
|
||||
self.url = url
|
||||
self.roles = roles
|
||||
self.children = {}
|
||||
self.children = dict()
|
||||
self.parent = parent
|
||||
self.order = order if order is not None else float("inf")
|
||||
|
||||
|
@ -20,7 +20,7 @@ class Entry:
|
|||
return super().__lt__(other)
|
||||
|
||||
|
||||
class GASSite:
|
||||
class GASSite(object):
|
||||
base_role = 'admins'
|
||||
|
||||
def __init__(self):
|
||||
|
@ -42,12 +42,12 @@ class GASSite:
|
|||
exposed when gas is not active.
|
||||
"""
|
||||
if prefix in self._registry['urls']:
|
||||
raise ImproperlyConfigured(f"Prefix {prefix} already in use")
|
||||
raise ImproperlyConfigured("Prefix {0} already in use".format(prefix))
|
||||
self._registry['urls'][prefix] = urls
|
||||
|
||||
def register_menu(self, name, label, url=None, icon=None, roles=None, parent=None, order=None):
|
||||
if name in self._registry['menu']:
|
||||
raise ImproperlyConfigured(f"Menu entry '{name}' already registered.")
|
||||
raise ImproperlyConfigured("Menu entry '{0}' already registered.".format(name))
|
||||
|
||||
if roles is None:
|
||||
roles = set()
|
||||
|
@ -61,8 +61,8 @@ class GASSite:
|
|||
if parent:
|
||||
try:
|
||||
parent_entry = self._registry['menu'][parent]
|
||||
except KeyError as exc:
|
||||
raise ImproperlyConfigured("Parent {parent} not registered.") from exc
|
||||
except KeyError:
|
||||
raise ImproperlyConfigured("Parent {} not registered.".format(parent))
|
||||
parent_entry.children[name] = entry
|
||||
|
||||
self._registry['menu'][name] = entry
|
||||
|
@ -86,7 +86,7 @@ class GASSite:
|
|||
for prefix, urls in self._registry['urls'].items():
|
||||
if prefix:
|
||||
urlpatterns.append(
|
||||
re_path(f'^{prefix}/', include(urls)),
|
||||
re_path(r'^{0}/'.format(prefix), include(urls)),
|
||||
)
|
||||
else:
|
||||
urlpatterns.append(
|
||||
|
|
|
@ -262,7 +262,3 @@ label {
|
|||
width: 20em;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.preview {
|
||||
display: block;
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
{% load i18n %}{% autoescape off %}
|
||||
{% blocktranslate %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}.{% endblocktranslate %}
|
||||
|
||||
{% translate "Please go to the following page and choose a new password:" %}
|
||||
{% block reset_link %}
|
||||
{{ protocol }}://{{ domain }}{% url 'gas:password_reset_confirm' uidb64=uid token=token %}
|
||||
{% endblock %}
|
||||
{% translate 'Your username, in case you’ve forgotten:' %} {{ user.get_username }}
|
||||
|
||||
{% translate "Thanks for using our site!" %}
|
||||
|
||||
{% blocktranslate %}The {{ site_name }} team{% endblocktranslate %}
|
||||
|
||||
{% endautoescape %}
|
|
@ -1,20 +0,0 @@
|
|||
{% load i18n static form_tags %}<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{{ gas_title }} {% trans "Login" %}</title>
|
||||
{% for cssfile in css %}
|
||||
<link href="{% static cssfile %}" rel="stylesheet" type="text/css" />
|
||||
{% endfor %}
|
||||
</head>
|
||||
<body id="login">
|
||||
<p>{% translate 'Forgotten your password? Enter your email address below, and we’ll email instructions for setting a new one.' %}</p>
|
||||
<form action="." method="POST">{% csrf_token %}
|
||||
{% form_errors form %}
|
||||
{% form_field form.email %}
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<button type="submit">{% trans "Reset my password" %}</button>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -1,14 +0,0 @@
|
|||
{% load i18n static form_tags %}<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{{ gas_title }} {% trans "Login" %}</title>
|
||||
{% for cssfile in css %}
|
||||
<link href="{% static cssfile %}" rel="stylesheet" type="text/css" />
|
||||
{% endfor %}
|
||||
</head>
|
||||
<body id="login">
|
||||
<p>{% translate 'Your password was changed.' %}</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,26 +0,0 @@
|
|||
{% load i18n static form_tags %}<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{{ gas_title }} {% trans "Login" %}</title>
|
||||
{% for cssfile in css %}
|
||||
<link href="{% static cssfile %}" rel="stylesheet" type="text/css" />
|
||||
{% endfor %}
|
||||
</head>
|
||||
<body id="login">
|
||||
{% if validlink %}
|
||||
<p>{% translate "Please enter your new password twice so we can verify you typed it in correctly." %}</p>
|
||||
<form action="." method="POST">{% csrf_token %}
|
||||
<input class="hidden" autocomplete="username" value="{{ form.user.get_username }}">
|
||||
{% form_errors form %}
|
||||
{% form_field form.new_password1 %}
|
||||
{% form_field form.new_password2 %}
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
<button type="submit">{% trans "Change my password" %}</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>{% translate "The password reset link was invalid, possibly because it has already been used. Please request a new password reset." %}</p>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
|
@ -1,17 +0,0 @@
|
|||
{% load i18n static form_tags %}<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>{{ gas_title }} {% trans "Login" %}</title>
|
||||
{% for cssfile in css %}
|
||||
<link href="{% static cssfile %}" rel="stylesheet" type="text/css" />
|
||||
{% endfor %}
|
||||
</head>
|
||||
<body id="login">
|
||||
<p>{% translate 'We’ve emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly.' %}</p>
|
||||
<p>{% translate 'If you don’t receive an email, please make sure you’ve entered the address you registered with, and check your spam folder.' %}</p>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
{% if widget.is_initial %}{{ widget.initial_text }}: <a href="{{ widget.value.url }}"><img src="{{ widget.value.url }}" width="{{ widget.attrs.IMAGE_PREVIEW_WIDTH }}" class="preview" /></a>{% if not widget.required %}
|
||||
<input type="checkbox" name="{{ widget.checkbox_name }}" id="{{ widget.checkbox_id }}"{% if widget.attrs.disabled %} disabled{% endif %}>
|
||||
<label for="{{ widget.checkbox_id }}">{{ widget.clear_checkbox_label }}</label>{% endif %}<br>
|
||||
{{ widget.input_text }}:{% endif %}
|
||||
<input type="{{ widget.type }}" name="{{ widget.name }}"{% include "django/forms/widgets/attrs.html" %}>
|
|
@ -1,8 +1,7 @@
|
|||
from django import forms, template
|
||||
from django import forms
|
||||
from django import template
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
from ..gas_settings import IMAGE_PREVIEW_WIDTH
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
|
@ -20,10 +19,6 @@ def base_form_field(field, css=None, container_class='', add_another_url=None, f
|
|||
elif isinstance(field.field, forms.DateField):
|
||||
field.field.widget.input_type = 'date'
|
||||
|
||||
if isinstance(field.field, forms.ImageField):
|
||||
field.field.widget.template_name = 'gas/tags/widgets/image_preview.html'
|
||||
field.field.widget.attrs['IMAGE_PREVIEW_WIDTH'] = IMAGE_PREVIEW_WIDTH
|
||||
|
||||
if isinstance(field.field, forms.SplitDateTimeField):
|
||||
field.field.widget.widgets[1].attrs['placeholder'] = '00:00:00'
|
||||
else:
|
||||
|
|
|
@ -5,7 +5,6 @@ from django import template
|
|||
from django.utils.html import mark_safe
|
||||
|
||||
from ..sites import site
|
||||
from ..models import UserRole
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
@ -60,10 +59,3 @@ def pagination(request, page):
|
|||
@register.filter
|
||||
def to_json(data):
|
||||
return mark_safe(json.dumps(data))
|
||||
|
||||
|
||||
@register.filter
|
||||
def has_role(user, role):
|
||||
if user.is_superuser:
|
||||
return True
|
||||
return UserRole.objects.filter(role__in=(role, "admins"), user=user).exists()
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
from django.test import TestCase, Client
|
||||
from django.urls import reverse
|
||||
|
||||
from model_bakery import baker
|
||||
|
||||
|
||||
class GASLoginTestCase(TestCase):
|
||||
def test_load(self):
|
||||
client = Client()
|
||||
response = client.get(reverse("gas:login"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class IndexTestCase(TestCase):
|
||||
def test_load(self):
|
||||
admin_user = baker.make(
|
||||
"auth.User",
|
||||
username="admin",
|
||||
is_superuser=True,
|
||||
)
|
||||
|
||||
client = Client()
|
||||
response = client.get(reverse("gas:index"))
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
client.force_login(admin_user)
|
||||
response = client.get(reverse("gas:index"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
|
||||
class GASPasswordResetViewTestCase(TestCase):
|
||||
def test_load(self):
|
||||
client = Client()
|
||||
response = client.get(reverse("gas:reset_password"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "gas/reset.html")
|
||||
|
||||
|
||||
class GASPasswordResetDoneViewTestCase(TestCase):
|
||||
def test_load(self):
|
||||
client = Client()
|
||||
response = client.get(reverse("gas:password_reset_done"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "gas/reset_done.html")
|
||||
|
||||
|
||||
class GASPasswordResetConfirmViewTestCase(TestCase):
|
||||
def test_load(self):
|
||||
client = Client()
|
||||
response = client.get(reverse("gas:password_reset_confirm", kwargs={"uidb64": "uidb64", "token": "token"}))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "gas/reset_confirm.html")
|
||||
|
||||
|
||||
class GASPasswordResetCompleteViewTestCase(TestCase):
|
||||
def test_load(self):
|
||||
client = Client()
|
||||
response = client.get(reverse("gas:password_reset_complete"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTemplateUsed(response, "gas/reset_complete.html")
|
20
gas/utils.py
20
gas/utils.py
|
@ -6,13 +6,13 @@ from django.utils.functional import Promise
|
|||
|
||||
|
||||
class JSONEncoder(json.JSONEncoder):
|
||||
def default(self, o):
|
||||
if isinstance(o, datetime.datetime):
|
||||
return o.strftime('%Y-%m-%d %H:%M')
|
||||
if isinstance(o, datetime.date):
|
||||
return o.strftime('%Y-%m-%d')
|
||||
if isinstance(o, QuerySet):
|
||||
return list(o)
|
||||
if isinstance(o, Promise):
|
||||
return str(o)
|
||||
return json.JSONEncoder.default(self, o)
|
||||
def default(self, obj):
|
||||
if isinstance(obj, datetime.datetime):
|
||||
return obj.strftime('%Y-%m-%d %H:%M')
|
||||
if isinstance(obj, datetime.date):
|
||||
return obj.strftime('%Y-%m-%d')
|
||||
if isinstance(obj, QuerySet):
|
||||
return list(obj)
|
||||
if isinstance(obj, Promise):
|
||||
return str(obj)
|
||||
return json.JSONEncoder.default(self, obj)
|
||||
|
|
58
gas/views.py
58
gas/views.py
|
@ -1,10 +1,11 @@
|
|||
import json
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.admin.utils import NestedObjects
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import router
|
||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest, JsonResponse
|
||||
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
|
||||
from django.urls import reverse
|
||||
from django.utils.cache import add_never_cache_headers
|
||||
from django.utils.html import escape, escapejs
|
||||
from django.utils.text import capfirst
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
@ -43,26 +44,30 @@ class AjaxCommandsMixin:
|
|||
"""
|
||||
def post(self, request, *args, **kwargs):
|
||||
if 'command' in self.request.POST:
|
||||
command = self.request.POST['command']
|
||||
command_processor = getattr(self, f'do_{command}', None)
|
||||
command_processor = getattr(self, 'do_{0}'.format(self.request.POST['command']), None)
|
||||
if command_processor is not None:
|
||||
return command_processor()
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
else:
|
||||
handler = getattr(super(), 'post', self.http_method_not_allowed)
|
||||
return handler(request, *args, **kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if 'command' in self.request.GET:
|
||||
command = self.request.POST['command']
|
||||
command_processor = getattr(self, f'send_{command}', None)
|
||||
command_processor = getattr(self, 'send_{0}'.format(self.request.GET['command']), None)
|
||||
if command_processor is not None:
|
||||
return command_processor()
|
||||
else:
|
||||
return HttpResponseBadRequest()
|
||||
else:
|
||||
handler = getattr(super(), 'get', self.http_method_not_allowed)
|
||||
return handler(request, *args, **kwargs)
|
||||
|
||||
def render_json(self, data, encoder=utils.JSONEncoder):
|
||||
return JsonResponse(data, json_dumps_params={"indent": 2}, encoder=encoder)
|
||||
return HttpResponse(
|
||||
json.dumps(data, indent=2, cls=encoder),
|
||||
content_type='application/json')
|
||||
|
||||
|
||||
class GASMixin:
|
||||
|
@ -92,10 +97,7 @@ class GASMixin:
|
|||
|
||||
def dispatch(self, *args, **kwargs):
|
||||
if self.check_user_forbidden():
|
||||
path = self.request.path
|
||||
response = HttpResponseRedirect(reverse('gas:login') + f'?next={path}')
|
||||
add_never_cache_headers(response)
|
||||
return response
|
||||
return HttpResponseRedirect(reverse('gas:login') + '?next={}'.format(self.request.path))
|
||||
return super().dispatch(*args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
|
@ -117,6 +119,7 @@ class GASMixin:
|
|||
# Forcing possible reverse_lazy evaluation
|
||||
url = str(self.cancel_url)
|
||||
return url
|
||||
else:
|
||||
return self.get_success_url()
|
||||
|
||||
def get_continue_url(self):
|
||||
|
@ -124,6 +127,7 @@ class GASMixin:
|
|||
# Forcing possible reverse_lazy evaluation
|
||||
url = str(self.continue_url)
|
||||
return url
|
||||
else:
|
||||
raise ImproperlyConfigured("No URL to redirect to. Provide a continue_url.")
|
||||
|
||||
def get_header_title(self):
|
||||
|
@ -148,10 +152,10 @@ class GASMixin:
|
|||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
css = gas_settings.MEDIA['css']
|
||||
javascript = gas_settings.MEDIA['js']
|
||||
js = gas_settings.MEDIA['js']
|
||||
if gas_settings.EXTRA_MEDIA:
|
||||
css = css + gas_settings.EXTRA_MEDIA.get('css', [])
|
||||
javascript = javascript + gas_settings.EXTRA_MEDIA.get('js', [])
|
||||
js = js + gas_settings.EXTRA_MEDIA.get('js', [])
|
||||
ctx.update({
|
||||
'base_template': self.base_template,
|
||||
'home_url': self.get_home_url(),
|
||||
|
@ -163,7 +167,7 @@ class GASMixin:
|
|||
'gas_title': gas_settings.TITLE,
|
||||
'logo_static_url': gas_settings.LOGO,
|
||||
'css': css,
|
||||
'js': javascript,
|
||||
'js': js,
|
||||
})
|
||||
return ctx
|
||||
|
||||
|
@ -182,6 +186,7 @@ class GASListView(GASMixin, ListView):
|
|||
self.filter_form = self.get_filter_form()
|
||||
if self.filter_form is not None and self.filter_form.is_valid():
|
||||
return self.filter_form.filter(qs)
|
||||
else:
|
||||
return qs
|
||||
|
||||
def get_queryset(self):
|
||||
|
@ -211,15 +216,12 @@ class GASCreateView(GASMixin, CreateView):
|
|||
def form_valid(self, form):
|
||||
response = super().form_valid(form)
|
||||
if '_popup' in self.request.POST:
|
||||
# escape() calls force_text.
|
||||
obj_pk = escape(self.object.pk)
|
||||
obj = escapejs(self.object)
|
||||
return HttpResponse(
|
||||
'<!DOCTYPE html><html><head><title></title></head><body>'
|
||||
'<script type="text/javascript">'
|
||||
f' opener.dismissAddAnotherPopup(window, "{obj_pk}", "{obj}");'
|
||||
'</script></body></html>'
|
||||
)
|
||||
'<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script></body></html>' %
|
||||
# escape() calls force_text.
|
||||
(escape(self.object.pk), escapejs(self.object)))
|
||||
else:
|
||||
return response
|
||||
|
||||
|
||||
|
@ -253,14 +255,10 @@ class GASDeleteView(GASMixin, DeleteView):
|
|||
|
||||
def format_callback(obj):
|
||||
opts = obj._meta
|
||||
name = capfirst(opts.verbose_name)
|
||||
return f'{name}: {obj}'
|
||||
return '%s: %s' % (capfirst(opts.verbose_name), obj)
|
||||
|
||||
collector.collect([self.object])
|
||||
model_count = {
|
||||
model._meta.verbose_name_plural: len(objs)
|
||||
for model, objs in collector.model_objs.items()
|
||||
}
|
||||
model_count = {model._meta.verbose_name_plural: len(objs) for model, objs in collector.model_objs.items()}
|
||||
return collector.nested(format_callback), model_count
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -280,7 +278,7 @@ class GASDeleteView(GASMixin, DeleteView):
|
|||
})
|
||||
return ctx
|
||||
|
||||
def form_valid(self, form):
|
||||
response = super().form_valid(form)
|
||||
messages.add_message(self.request, messages.SUCCESS, self.get_deleted_text())
|
||||
def delete(self, request, *args, **kwargs):
|
||||
response = super().delete(request, *args, **kwargs)
|
||||
messages.add_message(request, messages.SUCCESS, self.get_deleted_text())
|
||||
return response
|
||||
|
|
|
@ -1 +1 @@
|
|||
Django>=3.2.13,<5.0
|
||||
Django==3.2.13
|
||||
|
|
Loading…
Reference in New Issue