Compare commits
No commits in common. "master" and "0.8.8" have entirely different histories.
|
@ -3,4 +3,3 @@
|
||||||
/django_gas.egg-info
|
/django_gas.egg-info
|
||||||
/.coverage
|
/.coverage
|
||||||
/htmlcov
|
/htmlcov
|
||||||
*.pyc
|
|
||||||
|
|
25
changelog.md
25
changelog.md
|
@ -1,31 +1,6 @@
|
||||||
Changelog
|
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
|
0.8.8
|
||||||
-----
|
-----
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,3 @@ Collaborators
|
||||||
=============
|
=============
|
||||||
|
|
||||||
* Shakarina
|
* Shakarina
|
||||||
* Leo Hakim
|
|
||||||
|
|
|
@ -8,9 +8,5 @@ urlpatterns = [
|
||||||
path('login/', views.GASLoginView.as_view(), name='login'),
|
path('login/', views.GASLoginView.as_view(), name='login'),
|
||||||
path('logout/', logout_then_login, {'login_url': 'gas:login'}, name='logout'),
|
path('logout/', logout_then_login, {'login_url': 'gas:login'}, name='logout'),
|
||||||
path('change-password/', views.GASPasswordChangeView.as_view(), name='change_password'),
|
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'),
|
path('', views.Index.as_view(), name='index'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,72 +1,45 @@
|
||||||
from django.contrib.auth.views import (
|
from django.contrib.auth.views import LoginView, PasswordChangeView
|
||||||
LoginView,
|
|
||||||
PasswordChangeView,
|
|
||||||
PasswordResetConfirmView,
|
|
||||||
PasswordResetDoneView,
|
|
||||||
PasswordResetView,
|
|
||||||
)
|
|
||||||
from django.shortcuts import resolve_url
|
from django.shortcuts import resolve_url
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from gas import gas_settings
|
|
||||||
from gas.views import GASMixin
|
from gas.views import GASMixin
|
||||||
|
|
||||||
|
from gas import gas_settings
|
||||||
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 Index(GASMixin, TemplateView):
|
class GASLoginView(LoginView):
|
||||||
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):
|
|
||||||
template_name = "gas/login.html"
|
template_name = "gas/login.html"
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
url = self.get_redirect_url()
|
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']
|
||||||
|
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 GASPasswordResetView(GASContextMixin, PasswordResetView):
|
class GASPasswordChangeView(GASMixin, PasswordChangeView):
|
||||||
template_name = "gas/reset.html"
|
template_name = 'gas/base_form.html'
|
||||||
html_email_template_name = "registration/password_reset_email.html"
|
success_url = reverse_lazy('gas:index')
|
||||||
success_url = reverse_lazy("gas:password_reset_done")
|
continue_url = reverse_lazy('gas:change_password')
|
||||||
|
title = _('Change your password')
|
||||||
|
success_message = _('Password changed.')
|
||||||
|
|
||||||
|
|
||||||
class GASPasswordResetDoneView(GASContextMixin, PasswordResetDoneView):
|
class Index(GASMixin, TemplateView):
|
||||||
template_name = "gas/reset_done.html"
|
main_menu = 'index'
|
||||||
|
template_name = "gas/index.html"
|
||||||
|
roles = ('staff',)
|
||||||
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"
|
|
||||||
|
|
|
@ -20,5 +20,3 @@ MEDIA = getattr(settings, 'GAS_MEDIA', {
|
||||||
})
|
})
|
||||||
|
|
||||||
EXTRA_MEDIA = getattr(settings, 'GAS_EXTRA_MEDIA', None)
|
EXTRA_MEDIA = getattr(settings, 'GAS_EXTRA_MEDIA', None)
|
||||||
|
|
||||||
IMAGE_PREVIEW_WIDTH = getattr(settings, 'IMAGE_PREVIEW_WIDTH', 240)
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ class GASSite:
|
||||||
for prefix, urls in self._registry['urls'].items():
|
for prefix, urls in self._registry['urls'].items():
|
||||||
if prefix:
|
if prefix:
|
||||||
urlpatterns.append(
|
urlpatterns.append(
|
||||||
re_path(f'^{prefix}/', include(urls)),
|
re_path(r'^{prefix}/', include(urls)),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
urlpatterns.append(
|
urlpatterns.append(
|
||||||
|
|
|
@ -262,7 +262,3 @@ label {
|
||||||
width: 20em;
|
width: 20em;
|
||||||
padding: 1em;
|
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 django.template.loader import render_to_string
|
||||||
|
|
||||||
from ..gas_settings import IMAGE_PREVIEW_WIDTH
|
|
||||||
|
|
||||||
register = template.Library()
|
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):
|
elif isinstance(field.field, forms.DateField):
|
||||||
field.field.widget.input_type = 'date'
|
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):
|
if isinstance(field.field, forms.SplitDateTimeField):
|
||||||
field.field.widget.widgets[1].attrs['placeholder'] = '00:00:00'
|
field.field.widget.widgets[1].attrs['placeholder'] = '00:00:00'
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -5,7 +5,6 @@ from django import template
|
||||||
from django.utils.html import mark_safe
|
from django.utils.html import mark_safe
|
||||||
|
|
||||||
from ..sites import site
|
from ..sites import site
|
||||||
from ..models import UserRole
|
|
||||||
|
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
@ -60,10 +59,3 @@ def pagination(request, page):
|
||||||
@register.filter
|
@register.filter
|
||||||
def to_json(data):
|
def to_json(data):
|
||||||
return mark_safe(json.dumps(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")
|
|
|
@ -1,6 +1,6 @@
|
||||||
[metadata]
|
[metadata]
|
||||||
name = django-gas
|
name = django-gas
|
||||||
version = 0.8.13
|
version = 0.8.8
|
||||||
description = An alternative to django admin
|
description = An alternative to django admin
|
||||||
long_description = file: readme.md, changelog.md, collaborators.md
|
long_description = file: readme.md, changelog.md, collaborators.md
|
||||||
long_description_content_type = text/markdown
|
long_description_content_type = text/markdown
|
||||||
|
|
Loading…
Reference in New Issue