general: update for compatibility with django 2.2 (#41626)
This commit is contained in:
parent
d340a2bc7e
commit
e01c978bec
|
@ -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,
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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',),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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, ...'))
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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',
|
||||
)
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
4
setup.py
4
setup.py
|
@ -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',
|
||||
|
|
|
@ -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']
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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')]
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
19
tox.ini
|
@ -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/}
|
||||
|
|
Reference in New Issue