general: update for compatibility with django 2.2 (#41626)

This commit is contained in:
Frédéric Péters 2020-04-12 11:05:05 +02:00
parent d340a2bc7e
commit e01c978bec
20 changed files with 90 additions and 68 deletions

View File

@ -45,8 +45,10 @@ class Migration(migrations.Migration):
name='Subscription',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('category', models.ForeignKey(verbose_name='category', to='corbo.Category')),
('user', models.ForeignKey(verbose_name='user', blank=True, to=settings.AUTH_USER_MODEL, null=True)),
('category', models.ForeignKey(verbose_name='category',
to='corbo.Category', on_delete=models.CASCADE)),
('user', models.ForeignKey(verbose_name='user', blank=True,
to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE)),
],
options={
},
@ -57,7 +59,7 @@ class Migration(migrations.Migration):
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('identifier', models.CharField(help_text='ex.: email, mobile phone number, jabber id', max_length=128, verbose_name='identifier', blank=True)),
('subscription', models.ForeignKey(to='corbo.Subscription')),
('subscription', models.ForeignKey(to='corbo.Subscription', on_delete=models.CASCADE)),
],
options={
},
@ -70,7 +72,8 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='announce',
name='category',
field=models.ForeignKey(verbose_name='category', to='corbo.Category'),
field=models.ForeignKey(verbose_name='category',
to='corbo.Category', on_delete=models.CASCADE),
preserve_default=True,
),
]

View File

@ -18,7 +18,8 @@ class Migration(migrations.Migration):
('channel', models.CharField(max_length=32, verbose_name='channel', choices=[(b'sms', 'SMS'), (b'email', 'Email')])),
('time', models.DateTimeField(auto_now_add=True, verbose_name='sent time')),
('result', models.TextField(verbose_name='result', blank=True)),
('announce', models.ForeignKey(verbose_name='announce', to='corbo.Announce')),
('announce', models.ForeignKey(verbose_name='announce',
to='corbo.Announce', on_delete=models.CASCADE)),
],
options={
'ordering': ('-time',),

View File

@ -33,7 +33,7 @@ class Migration(migrations.Migration):
migrations.AlterField(
model_name='subscription',
name='category',
field=models.ForeignKey(verbose_name='Category', to='corbo.Category'),
field=models.ForeignKey(verbose_name='Category', to='corbo.Category', on_delete=models.CASCADE),
preserve_default=True,
),
migrations.AlterUniqueTogether(

View File

@ -65,7 +65,8 @@ class Category(models.Model):
class Announce(models.Model):
category = models.ForeignKey('Category', verbose_name=_('category'))
category = models.ForeignKey('Category', verbose_name=_('category'),
on_delete=models.CASCADE)
title = models.CharField(_('title'), max_length=256,
help_text=_('maximum 256 characters'))
identifier = models.CharField(max_length=256, null=True, blank=True)
@ -150,7 +151,7 @@ class Announce(models.Model):
class Broadcast(models.Model):
announce = models.ForeignKey(Announce, verbose_name=_('announce'))
announce = models.ForeignKey(Announce, verbose_name=_('announce'), on_delete=models.CASCADE)
deliver_time = models.DateTimeField(_('Deliver time'), null=True)
delivery_count = models.IntegerField(_('Delivery count'), default=0)
@ -187,7 +188,7 @@ class Broadcast(models.Model):
class Subscription(models.Model):
category = models.ForeignKey('Category', verbose_name=_('Category'))
category = models.ForeignKey('Category', verbose_name=_('Category'), on_delete=models.CASCADE)
uuid = models.CharField(_('User identifier'), max_length=128, blank=True)
identifier = models.CharField(_('identifier'), max_length=128, blank=True,
help_text=_('ex.: mailto, ...'))

View File

@ -14,7 +14,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.forms.utils import flatatt
from django.template.loader import render_to_string
from django.utils.encoding import force_text
@ -24,10 +24,12 @@ from django.utils.translation import get_language
import ckeditor.widgets
def ckeditor_render(self, name, value, attrs=None):
def ckeditor_render(self, name, value, attrs=None, renderer=None):
if value is None:
value = ''
final_attrs = {'name': name}
if getattr(self, 'attrs', None):
final_attrs.update(self.attrs)
if attrs:
final_attrs.update(attrs)
if 'filebrowserUploadUrl' not in self.config:

View File

@ -42,12 +42,11 @@ INSTALLED_APPS = (
'rest_framework',
)
MIDDLEWARE_CLASSES = (
MIDDLEWARE = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

View File

@ -8,7 +8,7 @@ from django.views.decorators.cache import never_cache
from django.contrib.admin.views.decorators import staff_member_required
from .urls_utils import decorated_includes, manager_required
from .views import homepage, atom, unsubscribe, unsubscription_done, login, logout
from .views import homepage, atom, unsubscribe, unsubscription_done, LoginView, LogoutView
from .manage_urls import urlpatterns as manage_urls
from .api_urls import urlpatterns as api_urls
@ -21,14 +21,14 @@ urlpatterns = [
url(r'^atom$', atom, name='atom'),
url(r'^manage/', decorated_includes(manager_required,
include(manage_urls))),
url(r'^admin/', include(admin.site.urls)),
url(r'^admin/', admin.site.urls),
url(r'^api/', include(api_urls)),
url(r'^unsubscribe/done/$', unsubscription_done,
name='unsubscription_done'),
url(r'^unsubscribe/(?P<unsubscription_token>[\w:-]+)$', unsubscribe,
name='unsubscribe'),
url(r'^logout/$', logout, name='auth_logout'),
url(r'^login/$', login, name='auth_login'),
url(r'^logout/$',LogoutView.as_view(), name='auth_logout'),
url(r'^login/$', LoginView.as_view(), name='auth_login'),
url(r'^ckeditor/upload/', staff_member_required(ckeditor_views.upload),
name='ckeditor_upload'),
url(r'^ckeditor/browse/', never_cache(staff_member_required(ckeditor_views.browse)),

View File

@ -16,43 +16,52 @@
# Decorating URL includes, <https://djangosnippets.org/snippets/2532/>
import django
from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import RegexURLPattern, RegexURLResolver
class DecoratedURLPattern(RegexURLPattern):
if django.VERSION < (2, 0, 0):
from django.urls.resolvers import RegexURLPattern as URLPattern
from django.urls.resolvers import RegexURLResolver as URLResolver
else:
from django.urls.resolvers import URLPattern, URLResolver
class DecoratedURLPattern(URLPattern):
def resolve(self, *args, **kwargs):
result = super(DecoratedURLPattern, self).resolve(*args, **kwargs)
if result:
result.func = self._decorate_with(result.func)
return result
class DecoratedRegexURLResolver(RegexURLResolver):
class DecoratedURLResolver(URLResolver):
def resolve(self, *args, **kwargs):
result = super(DecoratedRegexURLResolver, self).resolve(*args, **kwargs)
result = super(DecoratedURLResolver, self).resolve(*args, **kwargs)
if result:
result.func = self._decorate_with(result.func)
return result
def decorated_includes(func, includes, *args, **kwargs):
urlconf_module, app_name, namespace = includes
for item in urlconf_module:
if isinstance(item, RegexURLPattern):
if isinstance(item, URLResolver):
item.__class__ = DecoratedURLResolver
else:
item.__class__ = DecoratedURLPattern
item._decorate_with = func
elif isinstance(item, RegexURLResolver):
item.__class__ = DecoratedRegexURLResolver
item._decorate_with = func
item._decorate_with = func
return urlconf_module, app_name, namespace
def manager_required(function=None, login_url=None):
def check_manager(user):
if user and user.is_staff:
return True
if user and not user.is_anonymous():
if user and not user.is_anonymous:
raise PermissionDenied()
# As the last resort, show the login form
return False

View File

@ -27,7 +27,7 @@ from django.conf import settings
from django.template import loader
from django.utils.translation import activate
from django.core.files.storage import DefaultStorage
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.core import signing
from django.utils.six.moves.urllib import parse as urlparse

View File

@ -4,11 +4,12 @@ from django.conf import settings
from django.contrib import messages
from django.core import signing
from django.utils import timezone
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.views.generic import CreateView, UpdateView, DeleteView, \
ListView, TemplateView, RedirectView, DetailView, FormView
from django.contrib.syndication.views import Feed
from django.shortcuts import resolve_url
from django.utils.decorators import method_decorator
from django.utils.encoding import force_text
from django.utils.feedgenerator import Atom1Feed as DjangoAtom1Feed
from django.utils.http import quote
@ -18,6 +19,7 @@ from django.contrib.auth import views as auth_views
from django.contrib import messages
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ngettext
from django.views.decorators.cache import never_cache
from . import models
from .forms import AnnounceForm, CategoryForm, SubscriptionsImportForm, \
@ -30,24 +32,23 @@ except ImportError:
get_idps = lambda: []
def login(request, *args, **kwargs):
if any(get_idps()):
if 'next' not in request.GET:
return HttpResponseRedirect(resolve_url('mellon_login'))
return HttpResponseRedirect(resolve_url('mellon_login') + '?next=' +
quote(request.GET.get('next')))
return auth_views.login(request, *args, **kwargs)
class LoginView(auth_views.LoginView):
def get(self, request, *args, **kwargs):
if any(get_idps()):
if not 'next' in request.GET:
return HttpResponseRedirect(resolve_url('mellon_login'))
return HttpResponseRedirect(
resolve_url('mellon_login') + '?next=' + quote(request.GET.get('next'))
)
return super(LoginView, self).get(request, *args, **kwargs)
def logout(request, next_page=None):
if any(get_idps()):
return HttpResponseRedirect(resolve_url('mellon_logout'))
auth_logout(request)
if next_page is not None:
next_page = resolve_url(next_page)
else:
next_page = '/'
return HttpResponseRedirect(next_page)
class LogoutView(auth_views.LogoutView):
@method_decorator(never_cache)
def dispatch(self, request, *args, **kwargs):
if any(get_idps()):
return HttpResponseRedirect(resolve_url('mellon_logout'))
return super(LogoutView, self).dispatch(request, *args, **kwargs)
class HomepageView(RedirectView):

View File

@ -83,7 +83,7 @@ class PickerWidgetMixin(object):
super(PickerWidgetMixin, self).__init__(attrs, format=self.format)
def render(self, name, value, attrs=None):
def render(self, name, value, attrs=None, renderer=None):
final_attrs = self.build_attrs(attrs)
rendered_widget = super(PickerWidgetMixin, self).render(name, value, final_attrs)

View File

@ -109,9 +109,9 @@ setup(
'Programming Language :: Python',
'Programming Language :: Python :: 2',
],
install_requires=['django>1.7, <1.12',
install_requires=['django>1.7, <2.3',
'django-ckeditor<4.5.4',
'djangorestframework>=3.3,<3.7',
'djangorestframework>=3.3,<3.8',
'html2text',
'gadjo',
'emails',

View File

@ -1,2 +1,4 @@
# Add corbo hobo agent
INSTALLED_APPS = ('corbo.hobo_agent', 'hobo.agent.common') + INSTALLED_APPS
REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] = ['rest_framework.authentication.BasicAuthentication']

View File

@ -6,7 +6,7 @@ import tempfile
from django.core.files.storage import DefaultStorage
from django.utils import timezone
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.conf import settings
from django.test import override_settings

View File

@ -4,7 +4,7 @@ import urllib
from uuid import uuid4
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.utils.http import urlencode
from django.contrib.auth import get_user_model
from django.utils.text import slugify
@ -50,7 +50,7 @@ def user():
def test_get_newsletters(app, categories, announces, user):
resp = app.get(reverse('newsletters'), status=403)
resp = app.get(reverse('newsletters'), status=(401, 403))
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.get(reverse('newsletters'))
data = resp.json
@ -65,9 +65,9 @@ def test_get_newsletters(app, categories, announces, user):
def test_get_subscriptions(app, categories, announces, user):
resp = app.get(reverse('subscriptions'), status=403)
resp = app.get(reverse('subscriptions'), status=(401, 403))
uuid = str(uuid4())
resp = app.get(reverse('subscriptions'), params={'uuid': uuid}, status=403)
resp = app.get(reverse('subscriptions'), params={'uuid': uuid}, status=(401, 403))
app.authorization = ('Basic', ('john.doe', 'password'))
for identifier, name in channel_choices:
@ -105,7 +105,7 @@ def test_update_subscriptions(app, categories, announces, user):
def test_delete_subscriptions(app, categories, announces, user):
params = urlencode({'uuid': str(uuid4())})
subscriptions_url = reverse('subscriptions') + '?' + params
resp = app.delete(subscriptions_url, status=403)
resp = app.delete(subscriptions_url, status=(401, 403))
app.authorization = ('Basic', ('john.doe', 'password'))
resp = app.delete(subscriptions_url)
if resp.json['data']:
@ -119,7 +119,7 @@ def test_simple_email_subscription(app, categories, user):
url = '/api/subscribe/?uuid=%s&email=john@example.net' % uuid
# anonymous
resp = app.post_json(url, params=payload, status=403)
resp = app.post_json(url, params=payload, status=(401, 403))
assert resp.json['detail'] == 'Authentication credentials were not provided.'
# authenticated

View File

@ -7,7 +7,7 @@ import mock
import random
import requests
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.core import mail, signing
from django.utils import timezone
from django.core.files.storage import DefaultStorage

View File

@ -2,6 +2,7 @@ import uuid
import pytest
import django
from django.db import connection
from django.db.migrations.executor import MigrationExecutor
@ -9,6 +10,8 @@ pytestmark = pytest.mark.django_db
def test_subscription_sms_identifier_format_migration():
if django.VERSION >= (2, 0, 0):
pytest.skip('NotSupportedError')
executor = MigrationExecutor(connection)
app = 'corbo'
migrate_from = [(app, '0009_auto_20170120_1533')]

View File

@ -3,7 +3,7 @@ import mock
import os
import pytest
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.contrib.auth.models import User
from django.test import override_settings

View File

@ -4,7 +4,7 @@ import pytest
from webtest import Upload
from django.utils.text import slugify
from django.core.urlresolvers import reverse
from django.urls import reverse
from django.contrib.auth import get_user_model
from corbo.models import Category, Subscription

19
tox.ini
View File

@ -1,6 +1,6 @@
[tox]
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/corbo/{env:BRANCH_NAME:}
envlist = py2-coverage-django111,py3-django111
envlist = py2-coverage-django111,py3-django111,py3-django22
[testenv]
usedevelop =
@ -10,16 +10,17 @@ setenv =
CORBO_SETTINGS_FILE=tests/settings.py
coverage: COVERAGE=--junitxml=test_results.xml --cov-report xml --cov-report html --cov=corbo/ --cov-config .coveragerc
deps =
django>=1.11,<1.12
django111: django>=1.11,<1.12
django22: django>=2.2,<2.3
http://git.entrouvert.org/hobo.git/snapshot/hobo-master.tar.gz
pytest-cov
pytest-django>=3.1.1,<3.4.6
pytest>=3.0.4
django-webtest<1.9.3
django-ckeditor<4.5.3
djangorestframework>=3.3,<3.7
pylint==1.4.0
astroid==1.3.2
pytest-django
pytest
django-webtest
git+http://git.entrouvert.org/debian/django-ckeditor.git
djangorestframework>=3.3,<3.8
pylint
astroid
mock
commands =
py.test {env:COVERAGE:} {posargs:tests/}