Browse Source

Move agreements from settings to agreement model

Rafał Pitoń 6 years ago
parent
commit
68922dcb5b

+ 15 - 9
misago/legal/context_processors.py

@@ -1,20 +1,26 @@
 from django.urls import reverse
 from django.urls import reverse
 
 
-from misago.conf import settings
+from .models import Agreement
 
 
 
 
 def legal_links(request):
 def legal_links(request):
+    agreements = Agreement.objects.get_agreements()
+
     legal_context = {}
     legal_context = {}
 
 
-    if settings.terms_of_service_link:
-        legal_context['TERMS_OF_SERVICE_URL'] = settings.terms_of_service_link
-    elif settings.terms_of_service:
-        legal_context['TERMS_OF_SERVICE_URL'] = reverse('misago:terms-of-service')
+    terms_of_service = agreements.get(Agreement.TYPE_TOS)
+    if terms_of_service:
+        if terms_of_service['link']:
+            legal_context['TERMS_OF_SERVICE_URL'] = terms_of_service['link']
+        elif terms_of_service['text']:
+            legal_context['TERMS_OF_SERVICE_URL'] = reverse('misago:terms-of-service')
 
 
-    if settings.privacy_policy_link:
-        legal_context['PRIVACY_POLICY_URL'] = settings.privacy_policy_link
-    elif settings.privacy_policy:
-        legal_context['PRIVACY_POLICY_URL'] = reverse('misago:privacy-policy')
+    privacy_policy = agreements.get(Agreement.TYPE_PRIVACY)
+    if privacy_policy:
+        if privacy_policy['link']:
+            legal_context['PRIVACY_POLICY_URL'] = privacy_policy['link']
+        elif privacy_policy['text']:
+            legal_context['PRIVACY_POLICY_URL'] = reverse('misago:privacy-policy')
 
 
     if legal_context:
     if legal_context:
         request.frontend_context.update(legal_context)
         request.frontend_context.update(legal_context)

+ 2 - 2
misago/legal/migrations/0002_agreement_useragreement.py

@@ -22,14 +22,14 @@ class Migration(migrations.Migration):
             name='Agreement',
             name='Agreement',
             fields=[
             fields=[
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                 ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
-                ('type', models.CharField(choices=[('terms-of-service', 'Terms of service'), ('privacy-policy', 'Privacy policy')], db_index=True, default='terms-of-service', max_length=20)),
+                ('type', models.CharField(choices=[('terms_of_service', 'Terms of service'), ('privacy_policy', 'Privacy policy')], db_index=True, default='terms_of_service', max_length=20)),
                 ('title', models.CharField(blank=True, max_length=255, null=True)),
                 ('title', models.CharField(blank=True, max_length=255, null=True)),
                 ('link', models.URLField(blank=True, max_length=255, null=True)),
                 ('link', models.URLField(blank=True, max_length=255, null=True)),
                 ('text', models.TextField(blank=True, null=True)),
                 ('text', models.TextField(blank=True, null=True)),
                 ('is_active', models.BooleanField(default=False)),
                 ('is_active', models.BooleanField(default=False)),
                 ('created_on', models.DateTimeField(default=django.utils.timezone.now)),
                 ('created_on', models.DateTimeField(default=django.utils.timezone.now)),
                 ('created_by_name', models.CharField(blank=True, max_length=255, null=True)),
                 ('created_by_name', models.CharField(blank=True, max_length=255, null=True)),
-                ('last_modified_on', models.DateTimeField(default=django.utils.timezone.now)),
+                ('last_modified_on', models.DateTimeField(blank=True, null=True)),
                 ('last_modified_by_name', models.CharField(blank=True, max_length=255, null=True)),
                 ('last_modified_by_name', models.CharField(blank=True, max_length=255, null=True)),
                 ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
                 ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
                 ('last_modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
                 ('last_modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),

+ 84 - 0
misago/legal/migrations/0003_create_agreements_from_settings.py

@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.15 on 2018-08-16 14:22
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+from misago.conf.migrationutils import delete_settings_cache, migrate_settings_group
+from misago.legal.models import Agreement as MisagoAgreement
+
+
+_ = lambda s: s
+
+
+LEGAL_SETTINGS = [
+    'terms_of_service_title',
+    'terms_of_service_link',
+    'terms_of_service',
+    'privacy_policy_title',
+    'privacy_policy_link',
+    'privacy_policy',
+]
+
+
+def create_legal_settings_group(apps, schema_editor):
+    Agreement = apps.get_model('misago_legal', 'Agreement')
+    Setting = apps.get_model('misago_conf', 'Setting')
+    
+    legal_conf = {}
+    for setting in Setting.objects.filter(setting__in=LEGAL_SETTINGS):
+        legal_conf[setting.setting] = setting.dry_value
+
+    if legal_conf['terms_of_service'] or legal_conf['terms_of_service_link']:
+        Agreement.objects.create(
+            type=MisagoAgreement.TYPE_TOS,
+            title=legal_conf['terms_of_service_title'],
+            link=legal_conf['terms_of_service_link'],
+            text=legal_conf['terms_of_service'],
+            is_active=True,
+        )
+
+    if legal_conf['privacy_policy'] or legal_conf['privacy_policy_link']:
+        Agreement.objects.create(
+            type=MisagoAgreement.TYPE_PRIVACY,
+            title=legal_conf['privacy_policy_title'],
+            link=legal_conf['privacy_policy_link'],
+            text=legal_conf['privacy_policy'],
+            is_active=True,
+        )
+
+    MisagoAgreement.objects.invalidate_cache()
+
+
+def delete_deprecated_settings(apps, schema_editor):
+    migrate_settings_group(
+        apps, {
+            'key': 'legal',
+            'name': _("Legal information"),
+            'description': _("Those settings allow you to set additional legal information for your forum."),
+            'settings': [
+                {
+                    'setting': 'forum_footnote',
+                    'name': _("Footnote"),
+                    'description': _("Short message displayed in forum footer."),
+                    'legend': _("Forum footer"),
+                    'field_extra': {
+                        'max_length': 300,
+                    },
+                    'is_public': True,
+                },
+            ],
+        }
+    )
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('misago_legal', '0002_agreement_useragreement'),
+    ]
+
+    operations = [
+        migrations.RunPython(create_legal_settings_group),
+        migrations.RunPython(delete_deprecated_settings),
+    ]

+ 3 - 4
misago/legal/models.py

@@ -25,10 +25,9 @@ class AgreementManager(models.Manager):
 
 
     def get_agreements_from_db(self):
     def get_agreements_from_db(self):
         agreements = {}
         agreements = {}
-        for agreement in Agreement.objects.filter('is_active'):
+        for agreement in Agreement.objects.filter(is_active=True):
             agreements[agreement.type] = {
             agreements[agreement.type] = {
-                'type': agreement.type,
-                'title': agreement.title,
+                'title': agreement.get_final_title(),
                 'link': agreement.link,
                 'link': agreement.link,
                 'text': bool(agreement.text),
                 'text': bool(agreement.text),
             }
             }
@@ -61,7 +60,7 @@ class Agreement(models.Model):
         related_name='+',
         related_name='+',
     )
     )
     created_by_name = models.CharField(max_length=255, null=True, blank=True)
     created_by_name = models.CharField(max_length=255, null=True, blank=True)
-    last_modified_on = models.DateTimeField(default=timezone.now)
+    last_modified_on = models.DateTimeField(null=True, blank=True)
     last_modified_by = models.ForeignKey(
     last_modified_by = models.ForeignKey(
         settings.AUTH_USER_MODEL,
         settings.AUTH_USER_MODEL,
         blank=True,
         blank=True,

+ 10 - 0
misago/legal/tests/test_admin_views.py

@@ -69,6 +69,11 @@ class AgreementAdminViewsTests(AdminTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, 'Test Rules')
         self.assertContains(response, 'Test Rules')
 
 
+        test_agreement = Agreement.objects.get(type=Agreement.TYPE_TOS)
+        self.assertIsNone(test_agreement.last_modified_on)
+        self.assertIsNone(test_agreement.last_modified_by)
+        self.assertIsNone(test_agreement.last_modified_by_name)
+
     def test_new_view_change_active(self):
     def test_new_view_change_active(self):
         """new agreement view creates new active agreement"""
         """new agreement view creates new active agreement"""
         response = self.client.get(reverse('misago:admin:users:agreements:new'))
         response = self.client.get(reverse('misago:admin:users:agreements:new'))
@@ -137,6 +142,11 @@ class AgreementAdminViewsTests(AdminTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, 'Test Privacy')
         self.assertContains(response, 'Test Privacy')
 
 
+        updated_agreement = Agreement.objects.get(type=Agreement.TYPE_PRIVACY)
+        self.assertTrue(updated_agreement.last_modified_on)
+        self.assertEqual(updated_agreement.last_modified_by, self.user)
+        self.assertEqual(updated_agreement.last_modified_by_name, self.user.username)
+
     def test_edit_view_change_active(self):
     def test_edit_view_change_active(self):
         """edit agreement view sets new active"""
         """edit agreement view sets new active"""
         self.client.post(
         self.client.post(

+ 63 - 32
misago/legal/tests/test_views.py

@@ -1,9 +1,8 @@
 from django.test import TestCase
 from django.test import TestCase
 from django.urls import reverse
 from django.urls import reverse
 
 
-from misago.conf import settings
-
 from misago.legal.context_processors import legal_links
 from misago.legal.context_processors import legal_links
+from misago.legal.models import Agreement
 
 
 
 
 class MockRequest(object):
 class MockRequest(object):
@@ -12,23 +11,25 @@ class MockRequest(object):
 
 
 
 
 class PrivacyPolicyTests(TestCase):
 class PrivacyPolicyTests(TestCase):
+    def setUp(self):
+        Agreement.objects.invalidate_cache()
+
     def tearDown(self):
     def tearDown(self):
-        settings.reset_settings()
+        Agreement.objects.invalidate_cache()
 
 
     def test_404_on_no_policy(self):
     def test_404_on_no_policy(self):
         """policy view returns 404 when no policy is set"""
         """policy view returns 404 when no policy is set"""
-        self.assertFalse(settings.privacy_policy_link)
-        self.assertFalse(settings.privacy_policy)
-
         response = self.client.get(reverse('misago:privacy-policy'))
         response = self.client.get(reverse('misago:privacy-policy'))
         self.assertEqual(response.status_code, 404)
         self.assertEqual(response.status_code, 404)
 
 
     def test_301_on_link_policy(self):
     def test_301_on_link_policy(self):
         """policy view returns 302 redirect when link is set"""
         """policy view returns 302 redirect when link is set"""
-        settings.override_setting('privacy_policy_link', 'http://test.com')
-        settings.override_setting('privacy_policy', 'Lorem ipsum')
-        self.assertTrue(settings.privacy_policy_link)
-        self.assertTrue(settings.privacy_policy)
+        Agreement.objects.create(
+            type=Agreement.TYPE_PRIVACY,
+            link='http://test.com',
+            text='Lorem ipsum',
+            is_active=True,
+        )
 
 
         response = self.client.get(reverse('misago:privacy-policy'))
         response = self.client.get(reverse('misago:privacy-policy'))
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
@@ -36,10 +37,12 @@ class PrivacyPolicyTests(TestCase):
 
 
     def test_200_on_link_policy(self):
     def test_200_on_link_policy(self):
         """policy view returns 200 when custom tos content is set"""
         """policy view returns 200 when custom tos content is set"""
-        settings.override_setting('privacy_policy_title', 'Test Policy')
-        settings.override_setting('privacy_policy', 'Lorem ipsum dolor')
-        self.assertTrue(settings.privacy_policy_title)
-        self.assertTrue(settings.privacy_policy)
+        Agreement.objects.create(
+            type=Agreement.TYPE_PRIVACY,
+            title='Test Policy',
+            text='Lorem ipsum dolor',
+            is_active=True,
+        )
 
 
         response = self.client.get(reverse('misago:privacy-policy'))
         response = self.client.get(reverse('misago:privacy-policy'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
@@ -53,7 +56,12 @@ class PrivacyPolicyTests(TestCase):
 
 
     def test_context_processor_misago_policy(self):
     def test_context_processor_misago_policy(self):
         """context processor has TOS link to Misago view"""
         """context processor has TOS link to Misago view"""
-        settings.override_setting('privacy_policy', 'Lorem ipsum')
+        Agreement.objects.create(
+            type=Agreement.TYPE_PRIVACY,
+            text='Lorem ipsum',
+            is_active=True,
+        )
+
         context_dict = legal_links(MockRequest())
         context_dict = legal_links(MockRequest())
 
 
         self.assertEqual(context_dict, {
         self.assertEqual(context_dict, {
@@ -62,7 +70,12 @@ class PrivacyPolicyTests(TestCase):
 
 
     def test_context_processor_remote_policy(self):
     def test_context_processor_remote_policy(self):
         """context processor has TOS link to remote url"""
         """context processor has TOS link to remote url"""
-        settings.override_setting('privacy_policy_link', 'http://test.com')
+        agreement = Agreement.objects.create(
+            type=Agreement.TYPE_PRIVACY,
+            link='http://test.com',
+            is_active=True,
+        )
+
         context_dict = legal_links(MockRequest())
         context_dict = legal_links(MockRequest())
 
 
         self.assertEqual(context_dict, {
         self.assertEqual(context_dict, {
@@ -70,7 +83,9 @@ class PrivacyPolicyTests(TestCase):
         })
         })
 
 
         # set misago view too
         # set misago view too
-        settings.override_setting('privacy_policy', 'Lorem ipsum')
+        agreement.text = 'Lorem ipsum'
+        agreement.save()
+        
         context_dict = legal_links(MockRequest())
         context_dict = legal_links(MockRequest())
 
 
         self.assertEqual(context_dict, {
         self.assertEqual(context_dict, {
@@ -79,23 +94,25 @@ class PrivacyPolicyTests(TestCase):
 
 
 
 
 class TermsOfServiceTests(TestCase):
 class TermsOfServiceTests(TestCase):
+    def setUp(self):
+        Agreement.objects.invalidate_cache()
+
     def tearDown(self):
     def tearDown(self):
-        settings.reset_settings()
+        Agreement.objects.invalidate_cache()
 
 
     def test_404_on_no_tos(self):
     def test_404_on_no_tos(self):
         """TOS view returns 404 when no TOS is set"""
         """TOS view returns 404 when no TOS is set"""
-        self.assertFalse(settings.terms_of_service_link)
-        self.assertFalse(settings.terms_of_service)
-
         response = self.client.get(reverse('misago:terms-of-service'))
         response = self.client.get(reverse('misago:terms-of-service'))
         self.assertEqual(response.status_code, 404)
         self.assertEqual(response.status_code, 404)
 
 
     def test_301_on_link_tos(self):
     def test_301_on_link_tos(self):
         """TOS view returns 302 redirect when link is set"""
         """TOS view returns 302 redirect when link is set"""
-        settings.override_setting('terms_of_service_link', 'http://test.com')
-        settings.override_setting('terms_of_service', 'Lorem ipsum')
-        self.assertTrue(settings.terms_of_service_link)
-        self.assertTrue(settings.terms_of_service)
+        Agreement.objects.create(
+            type=Agreement.TYPE_TOS,
+            link='http://test.com',
+            text='Lorem ipsum',
+            is_active=True,
+        )
 
 
         response = self.client.get(reverse('misago:terms-of-service'))
         response = self.client.get(reverse('misago:terms-of-service'))
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
@@ -103,10 +120,12 @@ class TermsOfServiceTests(TestCase):
 
 
     def test_200_on_link_tos(self):
     def test_200_on_link_tos(self):
         """TOS view returns 200 when custom tos content is set"""
         """TOS view returns 200 when custom tos content is set"""
-        settings.override_setting('terms_of_service_title', 'Test ToS')
-        settings.override_setting('terms_of_service', 'Lorem ipsum dolor')
-        self.assertTrue(settings.terms_of_service_title)
-        self.assertTrue(settings.terms_of_service)
+        Agreement.objects.create(
+            type=Agreement.TYPE_TOS,
+            title='Test ToS',
+            text='Lorem ipsum dolor',
+            is_active=True,
+        )
 
 
         response = self.client.get(reverse('misago:terms-of-service'))
         response = self.client.get(reverse('misago:terms-of-service'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
@@ -120,7 +139,12 @@ class TermsOfServiceTests(TestCase):
 
 
     def test_context_processor_misago_tos(self):
     def test_context_processor_misago_tos(self):
         """context processor has TOS link to Misago view"""
         """context processor has TOS link to Misago view"""
-        settings.override_setting('terms_of_service', 'Lorem ipsum')
+        Agreement.objects.create(
+            type=Agreement.TYPE_TOS,
+            text='Lorem ipsum',
+            is_active=True,
+        )
+
         context_dict = legal_links(MockRequest())
         context_dict = legal_links(MockRequest())
 
 
         self.assertEqual(
         self.assertEqual(
@@ -131,7 +155,12 @@ class TermsOfServiceTests(TestCase):
 
 
     def test_context_processor_remote_tos(self):
     def test_context_processor_remote_tos(self):
         """context processor has TOS link to remote url"""
         """context processor has TOS link to remote url"""
-        settings.override_setting('terms_of_service_link', 'http://test.com')
+        agreement = Agreement.objects.create(
+            type=Agreement.TYPE_TOS,
+            link='http://test.com',
+            is_active=True,
+        )
+
         context_dict = legal_links(MockRequest())
         context_dict = legal_links(MockRequest())
 
 
         self.assertEqual(context_dict, {
         self.assertEqual(context_dict, {
@@ -139,7 +168,9 @@ class TermsOfServiceTests(TestCase):
         })
         })
 
 
         # set misago view too
         # set misago view too
-        settings.override_setting('terms_of_service', 'Lorem ipsum')
+        agreement.text = 'Lorem ipsum'
+        agreement.save()
+
         context_dict = legal_links(MockRequest())
         context_dict = legal_links(MockRequest())
 
 
         self.assertEqual(context_dict, {
         self.assertEqual(context_dict, {

+ 25 - 32
misago/legal/views/legal.py

@@ -1,20 +1,20 @@
 from hashlib import md5
 from hashlib import md5
 
 
-from django.http import Http404
-from django.shortcuts import redirect, render
+from django.conf import settings
+from django.shortcuts import get_object_or_404, redirect, render
 from django.utils.encoding import force_bytes
 from django.utils.encoding import force_bytes
-from django.utils.translation import ugettext as _
+from django.utils.text import slugify
 
 
-from misago.conf import settings
 from misago.core.cache import cache
 from misago.core.cache import cache
+from misago.legal.models import Agreement
 from misago.markup import common_flavour
 from misago.markup import common_flavour
 
 
 
 
-def get_parsed_content(request, setting_name):
-    cache_name = 'misago_legal_%s' % setting_name
+def get_parsed_content(request, agreement):
+    cache_name = 'misago_legal_%s_%s' % (agreement.pk, agreement.last_modified_on or '')
     cached_content = cache.get(cache_name)
     cached_content = cache.get(cache_name)
 
 
-    unparsed_content = settings.get_lazy_setting(setting_name)
+    unparsed_content = agreement.text
 
 
     checksum_source = force_bytes('%s:%s' % (unparsed_content, settings.SECRET_KEY))
     checksum_source = force_bytes('%s:%s' % (unparsed_content, settings.SECRET_KEY))
     unparsed_checksum = md5(checksum_source).hexdigest()
     unparsed_checksum = md5(checksum_source).hexdigest()
@@ -31,39 +31,32 @@ def get_parsed_content(request, setting_name):
         return cached_content['parsed']
         return cached_content['parsed']
 
 
 
 
-def privacy_policy(request):
-    if not (settings.privacy_policy or settings.privacy_policy_link):
-        raise Http404()
+def legal_view(request, agreement_type):
+    agreement = get_object_or_404(
+        Agreement, type=agreement_type, is_active=True
+    )
 
 
-    if settings.privacy_policy_link:
-        return redirect(settings.privacy_policy_link)
+    if agreement.link:
+        return redirect(agreement.link)
 
 
-    parsed_content = get_parsed_content(request, 'privacy_policy')
+    template_name = 'misago/{}.html'.format(agreement_type)
+    parsed_content = get_parsed_content(request, agreement)
 
 
     return render(
     return render(
-        request, 'misago/privacy_policy.html', {
-            'id': 'privacy-policy',
-            'title': settings.privacy_policy_title or _("Privacy policy"),
-            'link': settings.privacy_policy_link,
+        request,
+        template_name,
+        {
+            'id': slugify(agreement_type),
+            'title': agreement.get_final_title(),
+            'link': agreement.link,
             'body': parsed_content,
             'body': parsed_content,
         }
         }
     )
     )
 
 
 
 
-def terms_of_service(request):
-    if not (settings.terms_of_service or settings.terms_of_service_link):
-        raise Http404()
-
-    if settings.terms_of_service_link:
-        return redirect(settings.terms_of_service_link)
+def privacy_policy(request):
+    return legal_view(request, Agreement.TYPE_PRIVACY)
 
 
-    parsed_content = get_parsed_content(request, 'terms_of_service')
 
 
-    return render(
-        request, 'misago/terms_of_service.html', {
-            'id': 'terms-of-service',
-            'title': settings.terms_of_service_title or _("Terms of service"),
-            'link': settings.terms_of_service_link,
-            'body': parsed_content,
-        }
-    )
+def terms_of_service(request):
+    return legal_view(request, Agreement.TYPE_TOS)