diff --git a/hyperkitty-django4.patch b/hyperkitty-django4.patch new file mode 100644 index 0000000..4c8eb96 --- /dev/null +++ b/hyperkitty-django4.patch @@ -0,0 +1,331 @@ +commit e8228c7eddae75d168b28bc3a7abe351bab04b65 +Author: John Vandenberg +Date: Mon Dec 27 10:48:27 2021 +0800 + + Support Django 4.0 + +diff --git a/example_project/urls.py b/example_project/urls.py +index 9c854989..53245861 100644 +--- a/example_project/urls.py ++++ b/example_project/urls.py +@@ -21,21 +21,21 @@ This file is the main URL config for a Django website including HyperKitty. + """ + + from django.conf import settings +-from django.conf.urls import include, url ++from django.conf.urls import include + from django.contrib import admin + from django.urls import path, reverse_lazy + from django.views.generic import RedirectView + + + urlpatterns = [ +- url(r'^$', RedirectView.as_view( ++ path('', RedirectView.as_view( + url=reverse_lazy('hk_root'))), +- url(r'^hyperkitty/', include('hyperkitty.urls')), ++ path('hyperkitty/', include('hyperkitty.urls')), + # url(r'^postorius/', include('postorius.urls')), +- url(r'', include('django_mailman3.urls')), +- url(r'^accounts/', include('allauth.urls')), ++ path('', include('django_mailman3.urls')), ++ path('accounts/', include('allauth.urls')), + # Django admin +- url(r'^admin/', admin.site.urls), ++ path('admin/', admin.site.urls), + ] + + +diff --git a/hyperkitty/tests/urls_test.py b/hyperkitty/tests/urls_test.py +index 67d5497a..4e1c3af7 100644 +--- a/hyperkitty/tests/urls_test.py ++++ b/hyperkitty/tests/urls_test.py +@@ -21,11 +21,12 @@ + This file is the main URL config for a Django website including HyperKitty. + """ + +-from django.conf.urls import include, url ++from django.conf.urls import include ++from django.urls import path + + + urlpatterns = [ +- url(r'', include('hyperkitty.urls')), +- url(r'', include('django_mailman3.urls')), +- url(r'^accounts/', include('allauth.urls')), ++ path('', include('hyperkitty.urls')), ++ path('', include('django_mailman3.urls')), ++ path('accounts/', include('allauth.urls')), + ] +diff --git a/hyperkitty/urls.py b/hyperkitty/urls.py +index a6782fbd..f5e45e1a 100644 +--- a/hyperkitty/urls.py ++++ b/hyperkitty/urls.py +@@ -21,8 +21,9 @@ + # Author: Aurelien Bompard + # + +-from django.conf.urls import include, url ++from django.conf.urls import include + from django.contrib.staticfiles.urls import staticfiles_urlpatterns ++from django.urls import path, re_path + from django.views.generic.base import TemplateView + + from hyperkitty.api import email as api_email +@@ -41,146 +42,130 @@ from hyperkitty.views import ( + + # List archives and overview + list_patterns = [ +- url(r'^(?P\d{4})/(?P\d\d?)/(?P\d\d?)/$', ++ re_path(r'^(?P\d{4})/(?P\d\d?)/(?P\d\d?)/$', + mlist.archives, name='hk_archives_with_day'), +- url(r'^(?P\d{4})/(?P\d\d?)/$', ++ re_path(r'^(?P\d{4})/(?P\d\d?)/$', + mlist.archives, name='hk_archives_with_month'), +- url(r'^latest$', mlist.archives, name='hk_archives_latest'), +- url(r'^$', mlist.overview, name='hk_list_overview'), +- url(r'^recent-activity$', +- mlist.recent_activity, name='hk_list_recent_activity'), +- url(r'^recent-threads$', +- mlist.overview_recent_threads, name='hk_list_overview_recent_threads'), +- url(r'^pop-threads$', +- mlist.overview_pop_threads, name='hk_list_overview_pop_threads'), +- url(r'^top-threads$', +- mlist.overview_top_threads, name='hk_list_overview_top_threads'), +- url(r'^favorites$', +- mlist.overview_favorites, name='hk_list_overview_favorites'), +- url(r'^posted-to$', +- mlist.overview_posted_to, name='hk_list_overview_posted_to'), +- url(r'^top-posters$', +- mlist.overview_top_posters, name='hk_list_overview_top_posters'), +- url(r'^export/(?P[^/]+)\.mbox.gz$', ++ path('latest', mlist.archives, name='hk_archives_latest'), ++ path('', mlist.overview, name='hk_list_overview'), ++ path('recent-activity', mlist.recent_activity, name='hk_list_recent_activity'), ++ path('recent-threads', mlist.overview_recent_threads, name='hk_list_overview_recent_threads'), ++ path('pop-threads', mlist.overview_pop_threads, name='hk_list_overview_pop_threads'), ++ path('top-threads', mlist.overview_top_threads, name='hk_list_overview_top_threads'), ++ path('favorites', mlist.overview_favorites, name='hk_list_overview_favorites'), ++ path('posted-to', mlist.overview_posted_to, name='hk_list_overview_posted_to'), ++ path('top-posters', mlist.overview_top_posters, name='hk_list_overview_top_posters'), ++ re_path(r'^export/(?P[^/]+)\.mbox.gz$', + mlist.export_mbox, name='hk_list_export_mbox'), +- url(r'delete/', mlist.delete, name='hk_list_delete'), +- url(r'feed/', check_mlist_private(MailingListFeed()), name='hk_list_feed'), ++ path('delete/', mlist.delete, name='hk_list_delete'), ++ path('feed/', check_mlist_private(MailingListFeed()), name='hk_list_feed'), + ] + + + # Messages + message_patterns = [ +- url(r'^$', message.index, name='hk_message_index'), +- url(r'^attachment/(?P\d+)/(?P.+)$', +- message.attachment, name='hk_message_attachment'), +- url(r'^vote$', message.vote, name='hk_message_vote'), +- url(r'^reply$', message.reply, name='hk_message_reply'), +- url(r'^delete$', message.delete, name='hk_message_delete'), ++ path('', message.index, name='hk_message_index'), ++ path('attachment//', message.attachment, name='hk_message_attachment'), ++ path('vote', message.vote, name='hk_message_vote'), ++ path('reply', message.reply, name='hk_message_reply'), ++ path('delete', message.delete, name='hk_message_delete'), + ] + + + # Threads + thread_patterns = [ +- url(r'^$', thread.thread_index, name='hk_thread'), +- url(r'^replies$', thread.replies, name='hk_thread_replies'), +- url(r'^tags$', thread.tags, name='hk_tags'), +- url(r'^suggest-tags$', thread.suggest_tags, name='hk_suggest_tags'), +- url(r'^favorite$', thread.favorite, name='hk_favorite'), +- url(r'^category$', thread.set_category, name='hk_thread_set_category'), +- url(r'^reattach$', thread.reattach, name='hk_thread_reattach'), +- url(r'^reattach-suggest$', +- thread.reattach_suggest, name='hk_thread_reattach_suggest'), +- url(r'^delete$', message.delete, name='hk_thread_delete'), ++ path('', thread.thread_index, name='hk_thread'), ++ path('replies', thread.replies, name='hk_thread_replies'), ++ path('tags', thread.tags, name='hk_tags'), ++ path('suggest-tags', thread.suggest_tags, name='hk_suggest_tags'), ++ path('favorite', thread.favorite, name='hk_favorite'), ++ path('category', thread.set_category, name='hk_thread_set_category'), ++ path('reattach', thread.reattach, name='hk_thread_reattach'), ++ path('reattach-suggest', thread.reattach_suggest, name='hk_thread_reattach_suggest'), ++ path('delete', message.delete, name='hk_thread_delete'), + ] + + + # REST API + api_list_patterns = [ +- url(r'^$', +- api_mailinglist.MailingListDetail.as_view(), name="hk_api_mailinglist_detail"), +- url(r'^threads/$', +- api_thread.ThreadList.as_view(), name="hk_api_thread_list"), +- url(r'^thread/(?P[^/]+)/$', +- api_thread.ThreadDetail.as_view(), name="hk_api_thread_detail"), +- url(r'^emails/$', +- api_email.EmailList.as_view(), name="hk_api_email_list"), +- url(r'^email/(?P.*)/$', ++ path('', api_mailinglist.MailingListDetail.as_view(), name="hk_api_mailinglist_detail"), ++ path('threads/', api_thread.ThreadList.as_view(), name="hk_api_thread_list"), ++ path('thread//', api_thread.ThreadDetail.as_view(), name="hk_api_thread_detail"), ++ path('emails/', api_email.EmailList.as_view(), name="hk_api_email_list"), ++ re_path(r'^email/(?P.*)/$', + api_email.EmailDetail.as_view(), name="hk_api_email_detail"), +- url(r'^thread/(?P[^/]+)/emails/$', +- api_email.EmailList.as_view(), name="hk_api_thread_email_list"), ++ path('thread//emails/', api_email.EmailList.as_view(), name="hk_api_thread_email_list"), + ] + api_patterns = [ +- url(r'^$', TemplateView.as_view(template_name="hyperkitty/api.html")), +- url(r'^lists/$', +- api_mailinglist.MailingListList.as_view(), name="hk_api_mailinglist_list"), +- url(r'^list/(?P[^/@]+@[^/@]+)/', include(api_list_patterns)), +- url(r'^sender/(?P[^/]+)/emails/$', +- api_email.EmailListBySender.as_view(), name="hk_api_sender_email_list"), +- url(r'^tags/$', api_tag.TagList.as_view(), name="hk_api_tag_list"), ++ path('', TemplateView.as_view(template_name="hyperkitty/api.html")), ++ path('lists/', api_mailinglist.MailingListList.as_view(), name="hk_api_mailinglist_list"), ++ re_path(r'^list/(?P[^/@]+@[^/@]+)/', include(api_list_patterns)), ++ path('sender//emails/', api_email.EmailListBySender.as_view(), name="hk_api_sender_email_list"), ++ path('tags/', api_tag.TagList.as_view(), name="hk_api_tag_list"), + ] + + + urlpatterns = [ + # Index +- url(r'^$', index.index, name='hk_root'), +- url(r'^find-list$', index.find_list, name='hk_find_list'), ++ path('', index.index, name='hk_root'), ++ path('find-list', index.find_list, name='hk_find_list'), + + # User profile +- url(r'^profile/', include([ +- url(r'^$', accounts.user_profile, name='hk_user_profile'), +- url(r'^favorites$', accounts.favorites, name='hk_user_favorites'), +- url(r'^last_views$', accounts.last_views, name='hk_user_last_views'), +- url(r'^votes$', accounts.votes, name='hk_user_votes'), +- url(r'^subscriptions$', accounts.subscriptions, ++ path('profile/', include([ ++ path('', accounts.user_profile, name='hk_user_profile'), ++ path('favorites', accounts.favorites, name='hk_user_favorites'), ++ path('last_views', accounts.last_views, name='hk_user_last_views'), ++ path('votes', accounts.votes, name='hk_user_votes'), ++ path('subscriptions', accounts.subscriptions, + name='hk_user_subscriptions'), + ])), + + # Users +- url(r'^users/$', users.users, name='hk_users_overview'), +- url(r'^users/(?P[^/]+)/$', accounts.public_profile, name='hk_public_user_profile'), +- url(r'^users/(?P[^/]+)/posts$', accounts.posts, name='hk_user_posts'), ++ path('users/', users.users, name='hk_users_overview'), ++ path('users//', accounts.public_profile, name='hk_public_user_profile'), ++ path('users//posts', accounts.posts, name='hk_user_posts'), + + # List archives and overview +- url(r'^list/(?P[^/@]+@[^/@]+)/', include(list_patterns)), ++ re_path(r'^list/(?P[^/@]+@[^/@]+)/', include(list_patterns)), + + # Messages +- url(r'^list/(?P[^/@]+@[^/@]+)/message/' ++ re_path(r'^list/(?P[^/@]+@[^/@]+)/message/' + r'(?P\w+)/', include(message_patterns)), +- url(r'^list/(?P[^/@]+@[^/@]+)/message/new$', ++ re_path(r'^list/(?P[^/@]+@[^/@]+)/message/new$', + message.new_message, name='hk_message_new'), + + # Threads +- url(r'^list/(?P[^/@]+@[^/@]+)/thread/(?P\w+)/', ++ re_path(r'^list/(?P[^/@]+@[^/@]+)/thread/(?P\w+)/', + include(thread_patterns)), + + # Search +- url(r'^search$', search.search, name='hk_search'), ++ path('search', search.search, name='hk_search'), + + # Categories and Tags +- url(r'^categories/$', categories.categories, name='hk_categories_overview'), +- url(r'^tags/$', tags.tags, name='hk_tags_overview'), ++ path('categories/', categories.categories, name='hk_categories_overview'), ++ path('tags/', tags.tags, name='hk_tags_overview'), + + # Mailman archiver API +- url(r'^api/mailman/urls$', mailman.urls, name='hk_mailman_urls'), +- url(r'^api/mailman/archive$', mailman.archive, name='hk_mailman_archive'), ++ path('api/mailman/urls', mailman.urls, name='hk_mailman_urls'), ++ path('api/mailman/archive', mailman.archive, name='hk_mailman_archive'), + + # REST API +- url(r'^api/', include(api_patterns)), ++ path('api/', include(api_patterns)), + + # Mailman 2.X compatibility +- url(r'^listinfo/?$', compat.summary), +- url(r'^listinfo/(?P[^/]+)/?$', compat.summary), +- url(r'^pipermail/(?P[^/]+)/?$', compat.summary), +- url(r'^pipermail/(?P[^/]+)/(?P\d\d\d\d)-(?P\w+)/?$', compat.arch_month), +- url(r'^pipermail/(?P[^/]+)/(?P\d\d\d\d)-(?P\w+)/(?P[a-z]+)\.html$', compat.arch_month), +- url(r'^pipermail/(?P[^/]+)/(?P\d\d\d\d)-(?P\w+)\.txt.gz', compat.arch_month_mbox), ++ re_path(r'^listinfo/?$', compat.summary), ++ re_path(r'^listinfo/(?P[^/]+)/?$', compat.summary), ++ re_path(r'^pipermail/(?P[^/]+)/?$', compat.summary), ++ re_path(r'^pipermail/(?P[^/]+)/(?P\d\d\d\d)-(?P\w+)/?$', compat.arch_month), ++ re_path(r'^pipermail/(?P[^/]+)/(?P\d\d\d\d)-(?P\w+)/(?P[a-z]+)\.html$', compat.arch_month), ++ re_path(r'^pipermail/(?P[^/]+)/(?P\d\d\d\d)-(?P\w+)\.txt.gz', compat.arch_month_mbox), + #url(r'^pipermail/(?P[^/]+)/(?P\d\d\d\d)-(?P\w+)/(?P\d+)\.html$', compat.message), +- url(r'^list/(?P[^@]+)@[^/]+/(?P\d\d\d\d)-(?P\w+)/?$', compat.arch_month), ++ re_path(r'^list/(?P[^@]+)@[^/]+/(?P\d\d\d\d)-(?P\w+)/?$', compat.arch_month), + #url(r'^list/(?P[^@]+)@[^/]+/(?P\d\d\d\d)-(?P\w+)/(?P\d+)\.html$', compat.message), + + # URL compatibility with previous versions +- url(r'^list/(?P[^@/]+)/', compat.redirect_list_id), +- url(r'^lists/', compat.redirect_lists), ++ re_path(r'^list/(?P[^@/]+)/', compat.redirect_list_id), ++ path('lists/', compat.redirect_lists), + + ] + #) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) +diff --git a/hyperkitty/views/mailman.py b/hyperkitty/views/mailman.py +index 285e4cd2..9b6dfb7e 100644 +--- a/hyperkitty/views/mailman.py ++++ b/hyperkitty/views/mailman.py +@@ -27,13 +27,12 @@ from email import message_from_binary_file + from email.message import EmailMessage + from email.policy import default + from functools import wraps +-from urllib.parse import urljoin ++from urllib.parse import unquote, urljoin + + from django.conf import settings + from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation + from django.http import HttpResponse + from django.urls import reverse +-from django.utils.http import urlunquote + from django.views.decorators.csrf import csrf_exempt + from django.views.decorators.http import require_POST + +@@ -134,7 +133,7 @@ def _get_url(mlist_fqdn, msg_id=None): + msg_hash = get_message_id_hash(msg_id.strip().strip("<>")) + url = reverse('hk_message_index', kwargs={ + "mlist_fqdn": mlist_fqdn, "message_id_hash": msg_hash}) +- relative_url = urlunquote(url) ++ relative_url = unquote(url) + mail_domain = mlist_fqdn.split("@")[1] + try: + domain = MailDomain.objects.get( +diff --git a/setup.py b/setup.py +index c16294b0..827cef0b 100755 +--- a/setup.py ++++ b/setup.py +@@ -37,7 +37,7 @@ with open('hyperkitty/__init__.py') as fp: + + # Requirements + REQUIRES = [ +- "django>=2.2,<3.3", ++ "django>=2.2,<4.1", + "django_mailman3>=1.3.7", + "django-gravatar2>=1.0.6", + "djangorestframework>=3.0.0", diff --git a/python-HyperKitty.changes b/python-HyperKitty.changes index fb00dd5..b31bdeb 100644 --- a/python-HyperKitty.changes +++ b/python-HyperKitty.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Tue Dec 28 07:02:11 UTC 2021 - John Vandenberg + +- Add hyperkitty-django4.patch to support Django 4 + ------------------------------------------------------------------- Fri Dec 17 10:25:31 UTC 2021 - Matej Cepl diff --git a/python-HyperKitty.spec b/python-HyperKitty.spec index 6d64c00..612ddbc 100644 --- a/python-HyperKitty.spec +++ b/python-HyperKitty.spec @@ -59,6 +59,8 @@ Patch0: hyperkitty-settings.patch # PATCH-FIX-UPSTREAM hyperkitty-fix-mistune-2.0-imports.patch gl#mailman/hyperkitty#379 mcepl@suse.com # Two elements moved in mistune 2.0 Patch1: hyperkitty-fix-mistune-2.0-imports.patch +# PATCH-FIX-UPSTREAM hyperkitty-django4.patch gl#mailman/hyperkitty#384 jayvdb@gmail.com +Patch2: hyperkitty-django4.patch # BuildRequires: %{python_module django-debug-toolbar >= 2.2} BuildRequires: %{python_module isort} @@ -151,10 +153,13 @@ This package holds the uwsgi configuration. cp %{SOURCE30} . touch settings_local.py -# Copy exmaple_project to just build the static files +%patch2 -p1 + +# Copy example_project to just build the static files rsync -a example_project/* build_static_files -%autopatch -p1 +%patch0 -p1 +%patch1 -p1 %build sed -i 's|^#!/usr/bin/env.*|#!%{_bindir}/python3|' \