diff --git a/qgisfeedproject/templates/feeds/feed_item_detail.html b/qgisfeedproject/templates/feeds/feed_item_detail.html
index 8d9bf3e..8d45ec9 100644
--- a/qgisfeedproject/templates/feeds/feed_item_detail.html
+++ b/qgisfeedproject/templates/feeds/feed_item_detail.html
@@ -149,10 +149,10 @@
Share this:
-
+
{% post_to_facebook object_or_url link_class="button is-large is-light" %}
{% post_to_linkedin object_or_url link_class="button is-large is-light" %}
- {% post_to_telegram "{{feed_entry.title}} \n {{feed_entry.content}}" object_or_url link_class="button is-large is-light" %}
+ {% post_to_telegram "{{feed_entry.title}}: {{feed_entry.content}}" object_or_url link_class="button is-large is-light" %}
{% post_to_reddit feed_entry.title object_or_url link_class="button is-large is-light" %}
{% send_email feed_entry.title feed_entry.content object_or_url link_class="button is-large is-light" %}
From abfce92d50fb1c9d6b3971de9d32fed72b40d81a Mon Sep 17 00:00:00 2001
From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com>
Date: Tue, 18 Jun 2024 16:37:59 +0300
Subject: [PATCH 5/8] Split tests file into multiple files
---
qgisfeedproject/qgisfeed/tests.py | 615 ------------------
qgisfeedproject/qgisfeed/tests/__init__.py | 1 +
.../qgisfeed/tests/test_feed_entry.py | 220 +++++++
.../qgisfeed/tests/test_home_page.py | 53 ++
qgisfeedproject/qgisfeed/tests/test_login.py | 19 +
.../qgisfeed/tests/test_user_visit.py | 58 ++
qgisfeedproject/qgisfeed/tests/test_views.py | 293 +++++++++
7 files changed, 644 insertions(+), 615 deletions(-)
delete mode 100644 qgisfeedproject/qgisfeed/tests.py
create mode 100644 qgisfeedproject/qgisfeed/tests/__init__.py
create mode 100644 qgisfeedproject/qgisfeed/tests/test_feed_entry.py
create mode 100644 qgisfeedproject/qgisfeed/tests/test_home_page.py
create mode 100644 qgisfeedproject/qgisfeed/tests/test_login.py
create mode 100644 qgisfeedproject/qgisfeed/tests/test_user_visit.py
create mode 100644 qgisfeedproject/qgisfeed/tests/test_views.py
diff --git a/qgisfeedproject/qgisfeed/tests.py b/qgisfeedproject/qgisfeed/tests.py
deleted file mode 100644
index 29a9e04..0000000
--- a/qgisfeedproject/qgisfeed/tests.py
+++ /dev/null
@@ -1,615 +0,0 @@
-# coding=utf-8
-""""Tests for QGIS Welcome Page News Feed requests
-
-.. note:: This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
-"""
-
-__author__ = 'elpaso@itopen.it'
-__date__ = '2019-05-07'
-__copyright__ = 'Copyright 2019, ItOpen'
-
-import json
-
-from django.test import TestCase
-from django.test import Client
-from django.contrib.auth.models import Group, User
-from django.contrib.admin.sites import AdminSite
-from django.utils import timezone
-from django.core.paginator import Page
-from django.urls import reverse
-from django.contrib.gis.geos import Polygon
-from django.core.files.uploadedfile import SimpleUploadedFile
-from django.conf import settings
-from django.db import connection
-from django.core import mail
-
-from .utils import get_field_max_length
-
-from .models import (
- CharacterLimitConfiguration, QgisFeedEntry, QgisUserVisit, DailyQgisUserVisit, aggregate_user_visit_data
-)
-from .admin import QgisFeedEntryAdmin
-
-from os.path import join
-
-class MockRequest:
-
- def build_absolute_uri(self, uri):
- return uri
-
-class MockSuperUser:
-
- def is_superuser(self):
- return True
-
- def has_perm(self, perm):
- return True
-
-class MockStaff:
-
- def is_superuser(self):
- return False
-
- def is_staff(self):
- return True
-
- def has_perm(self, perm):
- return True
-
-
-request = MockRequest()
-
-
-class QgisFeedEntryTestCase(TestCase):
- fixtures = ['qgisfeed.json', 'users.json']
-
- def setUp(self):
- pass
-
- def test_sorting(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/')
- data = json.loads(response.content)
- data[0]['title'] = "Next Microsoft Windows code name revealed"
-
- def test_unpublished(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/')
- data = json.loads(response.content)
- titles = [d['title'] for d in data]
- self.assertFalse("QGIS core will be rewritten in FORTRAN" in titles)
-
- def test_published(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/')
- data = json.loads(response.content)
- titles = [d['title'] for d in data]
- self.assertTrue("QGIS core will be rewritten in Rust" in titles)
-
- def test_expired(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/')
- data = json.loads(response.content)
- titles = [d['title'] for d in data]
- self.assertFalse("QGIS core will be rewritten in PASCAL" in titles)
- self.assertFalse("QGIS core will be rewritten in GO" in titles)
-
- def test_future(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/')
- data = json.loads(response.content)
- titles = [d['title'] for d in data]
- self.assertFalse("QGIS core will be rewritten in BASIC" in titles)
-
- def test_lang_filter(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/?lang=fr')
- data = json.loads(response.content)
- titles = [d['title'] for d in data]
- self.assertFalse("Null Island QGIS Meeting" in titles)
- self.assertTrue("QGIS acquired by ESRI" in titles)
-
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/?lang=en')
- data = json.loads(response.content)
- titles = [d['title'] for d in data]
- self.assertTrue("Null Island QGIS Meeting" in titles)
- self.assertTrue("QGIS acquired by ESRI" in titles)
-
- def test_lat_lon_filter(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/?lat=0&lon=0')
- data = json.loads(response.content)
- titles = [d['title'] for d in data]
- self.assertTrue("Null Island QGIS Meeting" in titles)
- self.assertFalse("QGIS Italian Meeting" in titles)
-
- response = c.get('/?lat=44.5&lon=9.5')
- data = json.loads(response.content)
- titles = [d['title'] for d in data]
- self.assertFalse("Null Island QGIS Meeting" in titles)
- self.assertTrue("QGIS Italian Meeting" in titles)
-
- def test_after(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/?after=%s' % timezone.datetime(2019, 5, 9).timestamp())
- data = json.loads(response.content)
- titles = [d['title'] for d in data]
- self.assertFalse("Null Island QGIS Meeting" in titles)
- self.assertTrue("QGIS Italian Meeting" in titles)
-
- # Check that an updated entry is added to the feed even if
- # expired, but only with QGIS >= 3.36
- with connection.cursor() as cursor:
- cursor.execute("UPDATE qgisfeed_qgisfeedentry SET publish_to='2019-04-09', modified = '2019-05-10', title='Null Island QGIS Hackfest' WHERE title='Null Island QGIS Meeting'")
-
- response = c.get('/?after=%s' % timezone.datetime(2019, 5, 9).timestamp())
- titles = [d['title'] for d in data]
- self.assertFalse("Null Island QGIS Meeting" in titles)
-
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/33600/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/?after=%s' % timezone.datetime(2019, 5, 9).timestamp())
- data = json.loads(response.content)
- null_island = [d for d in data if d['title'] == "Null Island QGIS Hackfest"][0]
- self.assertTrue(timezone.datetime(2019, 5, 9).timestamp() > null_island['publish_to'])
-
-
- def test_invalid_parameters(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/?lat=ZZ&lon=KK')
- self.assertEqual(response.status_code, 400)
- response = c.get('/?lang=KK')
- self.assertEqual(response.status_code, 400)
-
- def test_image_link(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/')
- data = json.loads(response.content)
- image = [d['image'] for d in data if d['image'] != ""][0]
- self.assertEqual(image, "http://testserver/media/feedimages/rust.png" )
-
- def test_sticky(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- response = c.get('/')
- data = json.loads(response.content)
- sticky = data[0]
- self.assertTrue(sticky['sticky'])
- not_sticky = data[-1]
- self.assertFalse(not_sticky['sticky'])
-
- def test_group_is_created(self):
- self.assertEqual(Group.objects.filter(name='qgisfeedentry_authors').count(), 1)
- perms = sorted([p.codename for p in Group.objects.get(name='qgisfeedentry_authors').permissions.all()])
- self.assertEqual(perms, ['add_qgisfeedentry', 'view_qgisfeedentry'])
- # Create a staff user and verify
- staff = User(username='staff_user', is_staff=True)
- staff.save()
- self.assertIsNotNone(staff.groups.get(name='qgisfeedentry_authors'))
- self.assertEqual(staff.get_all_permissions(), set(('qgisfeed.add_qgisfeedentry', 'qgisfeed.view_qgisfeedentry')))
-
- def test_admin_publish_from(self):
- """Test that published entries have publish_from set"""
-
- site = AdminSite()
- ma = QgisFeedEntryAdmin(QgisFeedEntry, site)
- obj = QgisFeedEntry(title='Test entry')
- request.user = User.objects.get(username='admin')
- form = ma.get_form(request, obj)
- ma.save_model(request, obj, form, False)
- self.assertIsNone(obj.publish_from)
- self.assertFalse(obj.published)
- obj.published = True
- ma.save_model(request, obj, form, True)
- self.assertIsNotNone(obj.publish_from)
- self.assertTrue(obj.published)
-
- def test_admin_author_is_set(self):
- site = AdminSite()
- ma = QgisFeedEntryAdmin(QgisFeedEntry, site)
- obj = QgisFeedEntry(title='Test entry 2')
- request.user = User.objects.get(username='staff')
- form = ma.get_form(request, obj)
- ma.save_model(request, obj, form, False)
- self.assertEqual(obj.author, request.user)
-
-
-
-class HomePageTestCase(TestCase):
- """
- Test home page web version
- """
- fixtures = ['qgisfeed.json', 'users.json']
-
- def setUp(self):
- pass
-
- def test_authenticated_user_access(self):
- self.client.login(username='admin', password='admin')
-
- # Access the all view after logging in
- response = self.client.get(reverse('all'))
-
- # Check if the response status code is 200 (OK)
- self.assertEqual(response.status_code, 200)
-
- # Check if the correct template is used
- self.assertTemplateUsed(response, 'feeds/feed_home_page.html')
- self.assertTrue('form' in response.context)
-
-
- def test_unauthenticated_user_access(self):
- # Access the all view without logging in
- response = self.client.get(reverse('all'))
-
- # Check if the response status code is 200 (OK)
- self.assertEqual(response.status_code, 200)
-
- # Check if the correct template is used
- self.assertTemplateUsed(response, 'feeds/feed_home_page.html')
- self.assertTrue('form' in response.context)
-
- def test_feeds_list_filtering(self):
- # Test filter homepage feeds
-
- data = {
- 'lang': 'en',
- 'publish_from': '2023-12-31',
- }
- response = self.client.get(reverse('all'), data)
-
- # Check if the response status code is 200 (OK)
- self.assertEqual(response.status_code, 200)
-
- # Check if the correct template is used
- self.assertTemplateUsed(response, 'feeds/feed_home_page.html')
- self.assertTrue('form' in response.context)
-
-
-class QgisUserVisitTestCase(TestCase):
-
- def test_user_visit(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)')
- c.get('/')
- user_visit = QgisUserVisit.objects.filter(
- platform__icontains='Fedora Linux (Workstation Edition)')
- self.assertEqual(user_visit.count(), 1)
- self.assertEqual(user_visit.first().qgis_version, '32400')
-
- def test_ip_address_removed(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
- 'Linux (Workstation Edition)',
- REMOTE_ADDR='180.247.213.170')
- c.get('/')
- qgis_visit = QgisUserVisit.objects.first()
- self.assertTrue(qgis_visit.user_visit.remote_addr == '')
- self.assertTrue(qgis_visit.location['country_name'] == 'Indonesia')
-
- def test_aggregate_visit(self):
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/31400/Fedora '
- 'Linux (Workstation Edition)',
- REMOTE_ADDR='180.247.213.170')
- c.get('/')
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Windows 10',
- REMOTE_ADDR='180.247.213.160')
- c.get('/')
- c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Windows XP',
- REMOTE_ADDR='180.247.213.160')
- c.get('/')
- aggregate_user_visit_data()
- daily_visit = DailyQgisUserVisit.objects.first()
- self.assertTrue(daily_visit.platform['Windows 10'] == 1)
- self.assertTrue(daily_visit.qgis_version['32400'] == 2)
- self.assertTrue(daily_visit.country['ID'] == 3)
-
-
-class LoginTestCase(TestCase):
- """
- Test the login feature
- """
- fixtures = ['qgisfeed.json', 'users.json']
-
- def setUp(self):
- self.client = Client()
-
- def test_valid_login(self):
- response = self.client.login(username='admin', password='admin')
- self.assertTrue(response)
-
- def test_invalid_login(self):
- response = self.client.login(username='admin', password='wrongpassword')
- self.assertFalse(response)
-
-class FeedsListViewTestCase(TestCase):
- """
- Test the feeds list feature
- """
- fixtures = ['qgisfeed.json', 'users.json']
- def setUp(self):
- self.client = Client()
-
- def test_authenticated_user_access(self):
- self.client.login(username='admin', password='admin')
-
- # Access the feeds_list view after logging in
- response = self.client.get(reverse('feeds_list'))
-
- # Check if the response status code is 200 (OK)
- self.assertEqual(response.status_code, 200)
-
- # Check if the correct template is used
- self.assertTemplateUsed(response, 'feeds/feeds_list.html')
-
- def test_unauthenticated_user_redirect_to_login(self):
- # Access the feeds_list view without logging in
- response = self.client.get(reverse('feeds_list'))
-
- # Check if the response status code is 302 (Redirect)
- self.assertEqual(response.status_code, 302)
-
- # Check if the user is redirected to the login page
- self.assertRedirects(response, reverse('login') + '?next=' + reverse('feeds_list'))
-
-
- def test_nonstaff_user_redirect_to_login(self):
- user = User.objects.create_user(username='testuser', password='testpassword')
- self.client.login(username='testuser', password='testpassword')
- # Access the feeds_list view with a non staff user
- response = self.client.get(reverse('feeds_list'))
-
- # Check if the response status code is 302 (Redirect)
- self.assertEqual(response.status_code, 302)
-
- # Check if the user is redirected to the login page
- self.assertRedirects(response, reverse('login') + '?next=' + reverse('feeds_list'))
-
- def test_feeds_list_filtering(self):
- self.client.login(username='admin', password='admin')
- # Simulate a GET request with filter parameters
- data = {
- 'title': 'QGIS',
- 'author': 'admin',
- 'language_filter': 'en',
- 'publish_from': '2019-01-01',
- 'publish_to': '2023-12-31',
- 'sort_by': 'title',
- 'order': 'asc',
- }
- response = self.client.get(reverse('feeds_list'), data)
-
- # Check that the response status code is 200 (OK)
- self.assertEqual(response.status_code, 200)
-
- # Check that the response contains the expected context data
- self.assertTrue('feeds_entry' in response.context)
- self.assertTrue(isinstance(response.context['feeds_entry'], Page))
- self.assertTrue('sort_by' in response.context)
- self.assertTrue('order' in response.context)
- self.assertTrue('current_order' in response.context)
- self.assertTrue('form' in response.context)
- self.assertTrue('count' in response.context)
-
-class FeedsItemFormTestCase(TestCase):
- """
- Test the feeds add/update feature
- """
- fixtures = ['qgisfeed.json', 'users.json']
- def setUp(self):
- self.client = Client()
- spatial_filter = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
- image_path = join(settings.MEDIA_ROOT, "feedimages", "rust.png")
- self.post_data = {
- 'title': 'QGIS core will be rewritten in Rust',
- 'image': open(image_path, "rb"),
- 'content': 'Tired with C++ intricacies, the core developers have decided to rewrite QGIS in Rust',
- 'url': 'https://www.null.com',
- 'sticky': False,
- 'sorting': 0,
- 'language_filter': 'en',
- 'spatial_filter': str(spatial_filter),
- 'publish_from': '2023-10-18 14:46:00+00',
- 'publish_to': '2023-10-29 14:46:00+00'
- }
-
-
- def test_authenticated_user_access(self):
- self.client.login(username='admin', password='admin')
-
- # Access the feed_entry_add view after logging in
- response = self.client.get(reverse('feed_entry_add'))
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed(response, 'feeds/feed_item_form.html')
- self.assertTrue('form' in response.context)
-
- # Check if the approver has the permission.
- # Here, only the the admin user is listed.
- approvers = response.context['form']['approvers']
- self.assertEqual(len(approvers), 1)
- approver_id = int(approvers[0].data['value'])
- approver = User.objects.get(pk=approver_id)
- self.assertTrue(approver.has_perm("qgisfeed.publish_qgisfeedentry"))
-
- # Access the feed_entry_update view after logging in
- response = self.client.get(reverse('feed_entry_update', args=[3]))
- self.assertEqual(response.status_code, 200)
- self.assertTemplateUsed(response, 'feeds/feed_item_form.html')
- self.assertTrue('form' in response.context)
-
- def test_unauthenticated_user_redirect_to_login(self):
- # Access the feed_entry_add view without logging in
- response = self.client.get(reverse('feed_entry_add'))
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('login') + '?next=' + reverse('feed_entry_add'))
- self.assertIsNone(response.context)
-
- # Access the feed_entry_update view without logging in
- response = self.client.get(reverse('feed_entry_update', args=[3]))
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('login') + '?next=' + reverse('feed_entry_update', args=[3]))
- self.assertIsNone(response.context)
-
- def test_nonstaff_user_redirect_to_login(self):
- user = User.objects.create_user(username='testuser', password='testpassword')
- self.client.login(username='testuser', password='testpassword')
-
- # Access the feed_entry_add view with a non staff user
- response = self.client.get(reverse('feed_entry_add'))
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('login') + '?next=' + reverse('feed_entry_add'))
- self.assertIsNone(response.context)
-
- # Access the feed_entry_add view with a non staff user
- response = self.client.get(reverse('feed_entry_update', args=[3]))
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('login') + '?next=' + reverse('feed_entry_update', args=[3]))
- self.assertIsNone(response.context)
-
- def test_authenticated_user_add_feed(self):
- # Add a feed entry test
- self.client.login(username='staff', password='staff')
-
- response = self.client.post(reverse('feed_entry_add'), data=self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('feeds_list'))
-
-
- def test_authenticated_user_update_feed(self):
- # Update a feed entry test
- self.client.login(username='admin', password='admin')
-
- response = self.client.post(reverse('feed_entry_update', args=[3]), data=self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('feeds_list'))
-
-
- def test_not_allowed_user_update_feed(self):
- # Update a feed entry with a non allowed user
- self.client.login(username='staff', password='staff')
-
- response = self.client.post(reverse('feed_entry_update', args=[7]), data=self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('login') + '?next=' + reverse('feed_entry_update', args=[7]))
- self.assertIsNone(response.context)
-
- def test_allowed_user_publish_feed(self):
- # Publish a feed entry test
- self.client.login(username='admin', password='admin')
- self.post_data['publish'] = 1
- response = self.client.post(reverse('feed_entry_update', args=[7]), data=self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('feeds_list'))
-
- updated_data = QgisFeedEntry.objects.get(pk=7)
- self.assertTrue(updated_data.published)
-
- def test_allowed_staff_publish_feed(self):
- # Update a feed entry with an allowed staff user
- user = User.objects.get(username='staff')
- user.save()
- group = Group.objects.get(name='qgisfeedentry_approver')
- group.user_set.add(user)
-
- self.client.login(username='staff', password='staff')
- self.post_data['publish'] = 1
- response = self.client.post(reverse('feed_entry_update', args=[7]), data=self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('feeds_list'))
-
- updated_data = QgisFeedEntry.objects.get(pk=7)
- self.assertTrue(updated_data.published)
-
- def test_allowed_staff_unpublish_feed(self):
- # Update a feed entry with an allowed staff user
- user = User.objects.get(username='staff')
- user.save()
- group = Group.objects.get(name='qgisfeedentry_approver')
- group.user_set.add(user)
-
- self.client.login(username='staff', password='staff')
- self.post_data['publish'] = 0
-
- response = self.client.post(reverse('feed_entry_update', args=[7]), data=self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('feeds_list'))
-
- updated_data = QgisFeedEntry.objects.get(pk=7)
- self.assertFalse(updated_data.published)
-
- def test_authenticated_user_add_invalid_data(self):
- # Add a feed entry that contains invalid data
- self.client.login(username='staff', password='staff')
- spatial_filter = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
- image_path = join(settings.MEDIA_ROOT, "feedimages", "rust.png")
-
- # Limit content value to 10 characters
- config, created = CharacterLimitConfiguration.objects.update_or_create(
- field_name="content",
- max_characters=10
- )
-
- post_data = {
- 'title': '',
- 'image': open(image_path, "rb"),
- 'content': '
Tired with C++ intricacies, the core developers have decided to rewrite QGIS in Rust',
- 'url': '',
- 'sticky': False,
- 'sorting': 0,
- 'language_filter': 'en',
- 'spatial_filter': str(spatial_filter),
- 'publish_from': '',
- 'publish_to': ''
- }
-
- response = self.client.post(reverse('feed_entry_add'), data=post_data)
- self.assertEqual(response.status_code, 200)
- form = response.context['form']
- self.assertIn('title', form.errors, "This field is required.")
- self.assertIn('content', form.errors, "Ensure this value has at most 10 characters (it has 104).")
-
- def test_get_field_max_length(self):
- # Test the get_field_max_length function
- content_max_length = get_field_max_length(CharacterLimitConfiguration, field_name="content")
- self.assertEqual(content_max_length, 500)
- CharacterLimitConfiguration.objects.create(
- field_name="content",
- max_characters=1000
- )
- content_max_length = get_field_max_length(CharacterLimitConfiguration, field_name="content")
- self.assertEqual(content_max_length, 1000)
-
- def test_add_feed_with_reviewer(self):
- # Add a feed entry with specified reviewer test
- self.client.login(username='staff', password='staff')
- self.post_data['reviewers'] = [1]
-
- response = self.client.post(reverse('feed_entry_add'), data=self.post_data)
- self.assertEqual(response.status_code, 302)
- self.assertRedirects(response, reverse('feeds_list'))
-
- self.assertEqual(
- mail.outbox[0].recipients(),
- ['me@email.com']
- )
-
- self.assertEqual(
- mail.outbox[0].from_email,
- settings.QGISFEED_FROM_EMAIL
- )
-
diff --git a/qgisfeedproject/qgisfeed/tests/__init__.py b/qgisfeedproject/qgisfeed/tests/__init__.py
new file mode 100644
index 0000000..df954d6
--- /dev/null
+++ b/qgisfeedproject/qgisfeed/tests/__init__.py
@@ -0,0 +1 @@
+default_app_config = 'qgisfeed.apps.QgisFeedConfig'
\ No newline at end of file
diff --git a/qgisfeedproject/qgisfeed/tests/test_feed_entry.py b/qgisfeedproject/qgisfeed/tests/test_feed_entry.py
new file mode 100644
index 0000000..d6d5912
--- /dev/null
+++ b/qgisfeedproject/qgisfeed/tests/test_feed_entry.py
@@ -0,0 +1,220 @@
+""""Tests for QGIS Welcome Page News Feed requests
+
+.. note:: This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+"""
+
+__author__ = 'elpaso@itopen.it'
+__date__ = '2019-05-07'
+__copyright__ = 'Copyright 2019, ItOpen'
+
+import json
+
+from django.test import TestCase
+from django.test import Client
+from django.contrib.auth.models import Group, User
+from django.contrib.admin.sites import AdminSite
+from django.utils import timezone
+from django.db import connection
+
+from ..models import (
+ QgisFeedEntry
+)
+from ..admin import QgisFeedEntryAdmin
+
+
+class MockRequest:
+
+ def build_absolute_uri(self, uri):
+ return uri
+
+class MockSuperUser:
+
+ def is_superuser(self):
+ return True
+
+ def has_perm(self, perm):
+ return True
+
+class MockStaff:
+
+ def is_superuser(self):
+ return False
+
+ def is_staff(self):
+ return True
+
+ def has_perm(self, perm):
+ return True
+
+
+request = MockRequest()
+
+
+class QgisFeedEntryTestCase(TestCase):
+ fixtures = ['qgisfeed.json', 'users.json']
+
+ def setUp(self):
+ pass
+
+ def test_sorting(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/')
+ data = json.loads(response.content)
+ data[0]['title'] = "Next Microsoft Windows code name revealed"
+
+ def test_unpublished(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/')
+ data = json.loads(response.content)
+ titles = [d['title'] for d in data]
+ self.assertFalse("QGIS core will be rewritten in FORTRAN" in titles)
+
+ def test_published(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/')
+ data = json.loads(response.content)
+ titles = [d['title'] for d in data]
+ self.assertTrue("QGIS core will be rewritten in Rust" in titles)
+
+ def test_expired(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/')
+ data = json.loads(response.content)
+ titles = [d['title'] for d in data]
+ self.assertFalse("QGIS core will be rewritten in PASCAL" in titles)
+ self.assertFalse("QGIS core will be rewritten in GO" in titles)
+
+ def test_future(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/')
+ data = json.loads(response.content)
+ titles = [d['title'] for d in data]
+ self.assertFalse("QGIS core will be rewritten in BASIC" in titles)
+
+ def test_lang_filter(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/?lang=fr')
+ data = json.loads(response.content)
+ titles = [d['title'] for d in data]
+ self.assertFalse("Null Island QGIS Meeting" in titles)
+ self.assertTrue("QGIS acquired by ESRI" in titles)
+
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/?lang=en')
+ data = json.loads(response.content)
+ titles = [d['title'] for d in data]
+ self.assertTrue("Null Island QGIS Meeting" in titles)
+ self.assertTrue("QGIS acquired by ESRI" in titles)
+
+ def test_lat_lon_filter(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/?lat=0&lon=0')
+ data = json.loads(response.content)
+ titles = [d['title'] for d in data]
+ self.assertTrue("Null Island QGIS Meeting" in titles)
+ self.assertFalse("QGIS Italian Meeting" in titles)
+
+ response = c.get('/?lat=44.5&lon=9.5')
+ data = json.loads(response.content)
+ titles = [d['title'] for d in data]
+ self.assertFalse("Null Island QGIS Meeting" in titles)
+ self.assertTrue("QGIS Italian Meeting" in titles)
+
+ def test_after(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/?after=%s' % timezone.datetime(2019, 5, 9).timestamp())
+ data = json.loads(response.content)
+ titles = [d['title'] for d in data]
+ self.assertFalse("Null Island QGIS Meeting" in titles)
+ self.assertTrue("QGIS Italian Meeting" in titles)
+
+ # Check that an updated entry is added to the feed even if
+ # expired, but only with QGIS >= 3.36
+ with connection.cursor() as cursor:
+ cursor.execute("UPDATE qgisfeed_qgisfeedentry SET publish_to='2019-04-09', modified = '2019-05-10', title='Null Island QGIS Hackfest' WHERE title='Null Island QGIS Meeting'")
+
+ response = c.get('/?after=%s' % timezone.datetime(2019, 5, 9).timestamp())
+ titles = [d['title'] for d in data]
+ self.assertFalse("Null Island QGIS Meeting" in titles)
+
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/33600/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/?after=%s' % timezone.datetime(2019, 5, 9).timestamp())
+ data = json.loads(response.content)
+ null_island = [d for d in data if d['title'] == "Null Island QGIS Hackfest"][0]
+ self.assertTrue(timezone.datetime(2019, 5, 9).timestamp() > null_island['publish_to'])
+
+
+ def test_invalid_parameters(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/?lat=ZZ&lon=KK')
+ self.assertEqual(response.status_code, 400)
+ response = c.get('/?lang=KK')
+ self.assertEqual(response.status_code, 400)
+
+ def test_image_link(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/')
+ data = json.loads(response.content)
+ image = [d['image'] for d in data if d['image'] != ""][0]
+ self.assertEqual(image, "http://testserver/media/feedimages/rust.png" )
+
+ def test_sticky(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ response = c.get('/')
+ data = json.loads(response.content)
+ sticky = data[0]
+ self.assertTrue(sticky['sticky'])
+ not_sticky = data[-1]
+ self.assertFalse(not_sticky['sticky'])
+
+ def test_group_is_created(self):
+ self.assertEqual(Group.objects.filter(name='qgisfeedentry_authors').count(), 1)
+ perms = sorted([p.codename for p in Group.objects.get(name='qgisfeedentry_authors').permissions.all()])
+ self.assertEqual(perms, ['add_qgisfeedentry', 'view_qgisfeedentry'])
+ # Create a staff user and verify
+ staff = User(username='staff_user', is_staff=True)
+ staff.save()
+ self.assertIsNotNone(staff.groups.get(name='qgisfeedentry_authors'))
+ self.assertEqual(staff.get_all_permissions(), set(('qgisfeed.add_qgisfeedentry', 'qgisfeed.view_qgisfeedentry')))
+
+ def test_admin_publish_from(self):
+ """Test that published entries have publish_from set"""
+
+ site = AdminSite()
+ ma = QgisFeedEntryAdmin(QgisFeedEntry, site)
+ obj = QgisFeedEntry(title='Test entry')
+ request.user = User.objects.get(username='admin')
+ form = ma.get_form(request, obj)
+ ma.save_model(request, obj, form, False)
+ self.assertIsNone(obj.publish_from)
+ self.assertFalse(obj.published)
+ obj.published = True
+ ma.save_model(request, obj, form, True)
+ self.assertIsNotNone(obj.publish_from)
+ self.assertTrue(obj.published)
+
+ def test_admin_author_is_set(self):
+ site = AdminSite()
+ ma = QgisFeedEntryAdmin(QgisFeedEntry, site)
+ obj = QgisFeedEntry(title='Test entry 2')
+ request.user = User.objects.get(username='staff')
+ form = ma.get_form(request, obj)
+ ma.save_model(request, obj, form, False)
+ self.assertEqual(obj.author, request.user)
\ No newline at end of file
diff --git a/qgisfeedproject/qgisfeed/tests/test_home_page.py b/qgisfeedproject/qgisfeed/tests/test_home_page.py
new file mode 100644
index 0000000..fd7af60
--- /dev/null
+++ b/qgisfeedproject/qgisfeed/tests/test_home_page.py
@@ -0,0 +1,53 @@
+from django.test import TestCase
+from django.urls import reverse
+
+class HomePageTestCase(TestCase):
+ """
+ Test home page web version
+ """
+ fixtures = ['qgisfeed.json', 'users.json']
+
+ def setUp(self):
+ pass
+
+ def test_authenticated_user_access(self):
+ self.client.login(username='admin', password='admin')
+
+ # Access the all view after logging in
+ response = self.client.get(reverse('all'))
+
+ # Check if the response status code is 200 (OK)
+ self.assertEqual(response.status_code, 200)
+
+ # Check if the correct template is used
+ self.assertTemplateUsed(response, 'feeds/feed_home_page.html')
+ self.assertTrue('form' in response.context)
+
+
+ def test_unauthenticated_user_access(self):
+ # Access the all view without logging in
+ response = self.client.get(reverse('all'))
+
+ # Check if the response status code is 200 (OK)
+ self.assertEqual(response.status_code, 200)
+
+ # Check if the correct template is used
+ self.assertTemplateUsed(response, 'feeds/feed_home_page.html')
+ self.assertTrue('form' in response.context)
+
+ def test_feeds_list_filtering(self):
+ # Test filter homepage feeds
+
+ data = {
+ 'lang': 'en',
+ 'publish_from': '2023-12-31',
+ }
+ response = self.client.get(reverse('all'), data)
+
+ # Check if the response status code is 200 (OK)
+ self.assertEqual(response.status_code, 200)
+
+ # Check if the correct template is used
+ self.assertTemplateUsed(response, 'feeds/feed_home_page.html')
+ self.assertTrue('form' in response.context)
+
diff --git a/qgisfeedproject/qgisfeed/tests/test_login.py b/qgisfeedproject/qgisfeed/tests/test_login.py
new file mode 100644
index 0000000..0c99c72
--- /dev/null
+++ b/qgisfeedproject/qgisfeed/tests/test_login.py
@@ -0,0 +1,19 @@
+from django.test import TestCase
+from django.test import Client
+
+class LoginTestCase(TestCase):
+ """
+ Test the login feature
+ """
+ fixtures = ['qgisfeed.json', 'users.json']
+
+ def setUp(self):
+ self.client = Client()
+
+ def test_valid_login(self):
+ response = self.client.login(username='admin', password='admin')
+ self.assertTrue(response)
+
+ def test_invalid_login(self):
+ response = self.client.login(username='admin', password='wrongpassword')
+ self.assertFalse(response)
\ No newline at end of file
diff --git a/qgisfeedproject/qgisfeed/tests/test_user_visit.py b/qgisfeedproject/qgisfeed/tests/test_user_visit.py
new file mode 100644
index 0000000..b392454
--- /dev/null
+++ b/qgisfeedproject/qgisfeed/tests/test_user_visit.py
@@ -0,0 +1,58 @@
+""""Tests for QGIS Welcome Page News Feed requests
+
+.. note:: This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+"""
+
+__author__ = 'elpaso@itopen.it'
+__date__ = '2019-05-07'
+__copyright__ = 'Copyright 2019, ItOpen'
+
+from django.test import TestCase
+from django.test import Client
+
+from ..models import (
+ QgisUserVisit, DailyQgisUserVisit, aggregate_user_visit_data
+)
+
+
+class QgisUserVisitTestCase(TestCase):
+
+ def test_user_visit(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)')
+ c.get('/')
+ user_visit = QgisUserVisit.objects.filter(
+ platform__icontains='Fedora Linux (Workstation Edition)')
+ self.assertEqual(user_visit.count(), 1)
+ self.assertEqual(user_visit.first().qgis_version, '32400')
+
+ def test_ip_address_removed(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Fedora '
+ 'Linux (Workstation Edition)',
+ REMOTE_ADDR='180.247.213.170')
+ c.get('/')
+ qgis_visit = QgisUserVisit.objects.first()
+ self.assertTrue(qgis_visit.user_visit.remote_addr == '')
+ self.assertTrue(qgis_visit.location['country_name'] == 'Indonesia')
+
+ def test_aggregate_visit(self):
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/31400/Fedora '
+ 'Linux (Workstation Edition)',
+ REMOTE_ADDR='180.247.213.170')
+ c.get('/')
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Windows 10',
+ REMOTE_ADDR='180.247.213.160')
+ c.get('/')
+ c = Client(HTTP_USER_AGENT='Mozilla/5.0 QGIS/32400/Windows XP',
+ REMOTE_ADDR='180.247.213.160')
+ c.get('/')
+ aggregate_user_visit_data()
+ daily_visit = DailyQgisUserVisit.objects.first()
+ self.assertTrue(daily_visit.platform['Windows 10'] == 1)
+ self.assertTrue(daily_visit.qgis_version['32400'] == 2)
+ self.assertTrue(daily_visit.country['ID'] == 3)
+
diff --git a/qgisfeedproject/qgisfeed/tests/test_views.py b/qgisfeedproject/qgisfeed/tests/test_views.py
new file mode 100644
index 0000000..8ad46f8
--- /dev/null
+++ b/qgisfeedproject/qgisfeed/tests/test_views.py
@@ -0,0 +1,293 @@
+from django.test import TestCase
+from django.test import Client
+from django.contrib.auth.models import Group, User
+from django.core.paginator import Page
+from django.urls import reverse
+from django.contrib.gis.geos import Polygon
+from django.conf import settings
+from django.core import mail
+
+from ..utils import get_field_max_length
+
+from ..models import (
+ CharacterLimitConfiguration, QgisFeedEntry
+)
+
+from os.path import join
+
+class FeedsListViewTestCase(TestCase):
+ """
+ Test the feeds list feature
+ """
+ fixtures = ['qgisfeed.json', 'users.json']
+ def setUp(self):
+ self.client = Client()
+
+ def test_authenticated_user_access(self):
+ self.client.login(username='admin', password='admin')
+
+ # Access the feeds_list view after logging in
+ response = self.client.get(reverse('feeds_list'))
+
+ # Check if the response status code is 200 (OK)
+ self.assertEqual(response.status_code, 200)
+
+ # Check if the correct template is used
+ self.assertTemplateUsed(response, 'feeds/feeds_list.html')
+
+ def test_unauthenticated_user_redirect_to_login(self):
+ # Access the feeds_list view without logging in
+ response = self.client.get(reverse('feeds_list'))
+
+ # Check if the response status code is 302 (Redirect)
+ self.assertEqual(response.status_code, 302)
+
+ # Check if the user is redirected to the login page
+ self.assertRedirects(response, reverse('login') + '?next=' + reverse('feeds_list'))
+
+
+ def test_nonstaff_user_redirect_to_login(self):
+ user = User.objects.create_user(username='testuser', password='testpassword')
+ self.client.login(username='testuser', password='testpassword')
+ # Access the feeds_list view with a non staff user
+ response = self.client.get(reverse('feeds_list'))
+
+ # Check if the response status code is 302 (Redirect)
+ self.assertEqual(response.status_code, 302)
+
+ # Check if the user is redirected to the login page
+ self.assertRedirects(response, reverse('login') + '?next=' + reverse('feeds_list'))
+
+ def test_feeds_list_filtering(self):
+ self.client.login(username='admin', password='admin')
+ # Simulate a GET request with filter parameters
+ data = {
+ 'title': 'QGIS',
+ 'author': 'admin',
+ 'language_filter': 'en',
+ 'publish_from': '2019-01-01',
+ 'publish_to': '2023-12-31',
+ 'sort_by': 'title',
+ 'order': 'asc',
+ }
+ response = self.client.get(reverse('feeds_list'), data)
+
+ # Check that the response status code is 200 (OK)
+ self.assertEqual(response.status_code, 200)
+
+ # Check that the response contains the expected context data
+ self.assertTrue('feeds_entry' in response.context)
+ self.assertTrue(isinstance(response.context['feeds_entry'], Page))
+ self.assertTrue('sort_by' in response.context)
+ self.assertTrue('order' in response.context)
+ self.assertTrue('current_order' in response.context)
+ self.assertTrue('form' in response.context)
+ self.assertTrue('count' in response.context)
+
+class FeedsItemFormTestCase(TestCase):
+ """
+ Test the feeds add/update feature
+ """
+ fixtures = ['qgisfeed.json', 'users.json']
+ def setUp(self):
+ self.client = Client()
+ spatial_filter = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
+ image_path = join(settings.MEDIA_ROOT, "feedimages", "rust.png")
+ self.post_data = {
+ 'title': 'QGIS core will be rewritten in Rust',
+ 'image': open(image_path, "rb"),
+ 'content': '
Tired with C++ intricacies, the core developers have decided to rewrite QGIS in Rust',
+ 'url': 'https://www.null.com',
+ 'sticky': False,
+ 'sorting': 0,
+ 'language_filter': 'en',
+ 'spatial_filter': str(spatial_filter),
+ 'publish_from': '2023-10-18 14:46:00+00',
+ 'publish_to': '2023-10-29 14:46:00+00'
+ }
+
+
+ def test_authenticated_user_access(self):
+ self.client.login(username='admin', password='admin')
+
+ # Access the feed_entry_add view after logging in
+ response = self.client.get(reverse('feed_entry_add'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'feeds/feed_item_form.html')
+ self.assertTrue('form' in response.context)
+
+ # Check if the approver has the permission.
+ # Here, only the the admin user is listed.
+ approvers = response.context['form']['approvers']
+ self.assertEqual(len(approvers), 1)
+ approver_id = int(approvers[0].data['value'])
+ approver = User.objects.get(pk=approver_id)
+ self.assertTrue(approver.has_perm("qgisfeed.publish_qgisfeedentry"))
+
+ # Access the feed_entry_update view after logging in
+ response = self.client.get(reverse('feed_entry_update', args=[3]))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'feeds/feed_item_form.html')
+ self.assertTrue('form' in response.context)
+
+ def test_unauthenticated_user_redirect_to_login(self):
+ # Access the feed_entry_add view without logging in
+ response = self.client.get(reverse('feed_entry_add'))
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('login') + '?next=' + reverse('feed_entry_add'))
+ self.assertIsNone(response.context)
+
+ # Access the feed_entry_update view without logging in
+ response = self.client.get(reverse('feed_entry_update', args=[3]))
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('login') + '?next=' + reverse('feed_entry_update', args=[3]))
+ self.assertIsNone(response.context)
+
+ def test_nonstaff_user_redirect_to_login(self):
+ user = User.objects.create_user(username='testuser', password='testpassword')
+ self.client.login(username='testuser', password='testpassword')
+
+ # Access the feed_entry_add view with a non staff user
+ response = self.client.get(reverse('feed_entry_add'))
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('login') + '?next=' + reverse('feed_entry_add'))
+ self.assertIsNone(response.context)
+
+ # Access the feed_entry_add view with a non staff user
+ response = self.client.get(reverse('feed_entry_update', args=[3]))
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('login') + '?next=' + reverse('feed_entry_update', args=[3]))
+ self.assertIsNone(response.context)
+
+ def test_authenticated_user_add_feed(self):
+ # Add a feed entry test
+ self.client.login(username='staff', password='staff')
+
+ response = self.client.post(reverse('feed_entry_add'), data=self.post_data)
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('feeds_list'))
+
+
+ def test_authenticated_user_update_feed(self):
+ # Update a feed entry test
+ self.client.login(username='admin', password='admin')
+
+ response = self.client.post(reverse('feed_entry_update', args=[3]), data=self.post_data)
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('feeds_list'))
+
+
+ def test_not_allowed_user_update_feed(self):
+ # Update a feed entry with a non allowed user
+ self.client.login(username='staff', password='staff')
+
+ response = self.client.post(reverse('feed_entry_update', args=[7]), data=self.post_data)
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('login') + '?next=' + reverse('feed_entry_update', args=[7]))
+ self.assertIsNone(response.context)
+
+ def test_allowed_user_publish_feed(self):
+ # Publish a feed entry test
+ self.client.login(username='admin', password='admin')
+ self.post_data['publish'] = 1
+ response = self.client.post(reverse('feed_entry_update', args=[7]), data=self.post_data)
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('feeds_list'))
+
+ updated_data = QgisFeedEntry.objects.get(pk=7)
+ self.assertTrue(updated_data.published)
+
+ def test_allowed_staff_publish_feed(self):
+ # Update a feed entry with an allowed staff user
+ user = User.objects.get(username='staff')
+ user.save()
+ group = Group.objects.get(name='qgisfeedentry_approver')
+ group.user_set.add(user)
+
+ self.client.login(username='staff', password='staff')
+ self.post_data['publish'] = 1
+ response = self.client.post(reverse('feed_entry_update', args=[7]), data=self.post_data)
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('feeds_list'))
+
+ updated_data = QgisFeedEntry.objects.get(pk=7)
+ self.assertTrue(updated_data.published)
+
+ def test_allowed_staff_unpublish_feed(self):
+ # Update a feed entry with an allowed staff user
+ user = User.objects.get(username='staff')
+ user.save()
+ group = Group.objects.get(name='qgisfeedentry_approver')
+ group.user_set.add(user)
+
+ self.client.login(username='staff', password='staff')
+ self.post_data['publish'] = 0
+
+ response = self.client.post(reverse('feed_entry_update', args=[7]), data=self.post_data)
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('feeds_list'))
+
+ updated_data = QgisFeedEntry.objects.get(pk=7)
+ self.assertFalse(updated_data.published)
+
+ def test_authenticated_user_add_invalid_data(self):
+ # Add a feed entry that contains invalid data
+ self.client.login(username='staff', password='staff')
+ spatial_filter = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
+ image_path = join(settings.MEDIA_ROOT, "feedimages", "rust.png")
+
+ # Limit content value to 10 characters
+ config, created = CharacterLimitConfiguration.objects.update_or_create(
+ field_name="content",
+ max_characters=10
+ )
+
+ post_data = {
+ 'title': '',
+ 'image': open(image_path, "rb"),
+ 'content': '
Tired with C++ intricacies, the core developers have decided to rewrite QGIS in Rust',
+ 'url': '',
+ 'sticky': False,
+ 'sorting': 0,
+ 'language_filter': 'en',
+ 'spatial_filter': str(spatial_filter),
+ 'publish_from': '',
+ 'publish_to': ''
+ }
+
+ response = self.client.post(reverse('feed_entry_add'), data=post_data)
+ self.assertEqual(response.status_code, 200)
+ form = response.context['form']
+ self.assertIn('title', form.errors, "This field is required.")
+ self.assertIn('content', form.errors, "Ensure this value has at most 10 characters (it has 104).")
+
+ def test_get_field_max_length(self):
+ # Test the get_field_max_length function
+ content_max_length = get_field_max_length(CharacterLimitConfiguration, field_name="content")
+ self.assertEqual(content_max_length, 500)
+ CharacterLimitConfiguration.objects.create(
+ field_name="content",
+ max_characters=1000
+ )
+ content_max_length = get_field_max_length(CharacterLimitConfiguration, field_name="content")
+ self.assertEqual(content_max_length, 1000)
+
+ def test_add_feed_with_reviewer(self):
+ # Add a feed entry with specified reviewer test
+ self.client.login(username='staff', password='staff')
+ self.post_data['reviewers'] = [1]
+
+ response = self.client.post(reverse('feed_entry_add'), data=self.post_data)
+ self.assertEqual(response.status_code, 302)
+ self.assertRedirects(response, reverse('feeds_list'))
+
+ self.assertEqual(
+ mail.outbox[0].recipients(),
+ ['me@email.com']
+ )
+
+ self.assertEqual(
+ mail.outbox[0].from_email,
+ settings.QGISFEED_FROM_EMAIL
+ )
+
From 6fd59857ae7a46a1c0e1dcae2fc489abfadb45e6 Mon Sep 17 00:00:00 2001
From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com>
Date: Tue, 18 Jun 2024 17:07:27 +0300
Subject: [PATCH 6/8] Use the original django-social-share package
---
REQUIREMENTS.txt | 1 +
.../qgisfeed/templatetags/__init__.py | 0
.../qgisfeed/templatetags/social_share.py | 260 ------------------
.../qgisfeed/tests/test_feed_detail.py | 55 ++++
qgisfeedproject/qgisfeedproject/settings.py | 5 +-
.../templatetags/post_to_linkedin.html | 2 +-
6 files changed, 61 insertions(+), 262 deletions(-)
delete mode 100644 qgisfeedproject/qgisfeed/templatetags/__init__.py
delete mode 100644 qgisfeedproject/qgisfeed/templatetags/social_share.py
create mode 100644 qgisfeedproject/qgisfeed/tests/test_feed_detail.py
diff --git a/REQUIREMENTS.txt b/REQUIREMENTS.txt
index 5b4fe44..c5139da 100644
--- a/REQUIREMENTS.txt
+++ b/REQUIREMENTS.txt
@@ -10,6 +10,7 @@ Django==4.2.8
django-appconf==1.0.3
django-extensions==3.1.5
django-imagekit==5.0.0
+django-social-share==2.3.0
django-tinymce==3.6.1
django-user-visit==0.5.1
django-webpack-loader==2.0.1
diff --git a/qgisfeedproject/qgisfeed/templatetags/__init__.py b/qgisfeedproject/qgisfeed/templatetags/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/qgisfeedproject/qgisfeed/templatetags/social_share.py b/qgisfeedproject/qgisfeed/templatetags/social_share.py
deleted file mode 100644
index 0a3f38a..0000000
--- a/qgisfeedproject/qgisfeed/templatetags/social_share.py
+++ /dev/null
@@ -1,260 +0,0 @@
-# -*- coding: utf-8 -*-
-# This code is based on https://github.com/fcurella/django-social-share
-# Copyright (C) 2011 by Flavio Curella
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-
-from __future__ import unicode_literals
-
-import re
-
-from django import template
-
-from django.db.models import Model
-from django.template.defaultfilters import urlencode
-from django.utils.safestring import mark_safe
-
-
-register = template.Library()
-
-
-TWITTER_ENDPOINT = 'https://twitter.com/intent/tweet?text=%s'
-FACEBOOK_ENDPOINT = 'https://www.facebook.com/sharer/sharer.php?u=%s'
-GPLUS_ENDPOINT = 'https://plus.google.com/share?url=%s'
-MAIL_ENDPOINT = 'mailto:?subject=%s&body=%s'
-LINKEDIN_ENDPOINT = 'https://www.linkedin.com/shareArticle?mini=true&title=hello&url=%s'
-REDDIT_ENDPOINT = 'https://www.reddit.com/submit?title=%s&url=%s'
-TELEGRAM_ENDPOINT = 'https://t.me/share/url?text=%s&url=%s'
-WHATSAPP_ENDPOINT = 'https://api.whatsapp.com/send?text=%s'
-PINTEREST_ENDPOINT = 'https://www.pinterest.com/pin/create/button/?url=%s'
-
-
-BITLY_REGEX = re.compile(r'^https?://bit\.ly/')
-
-
-def compile_text(context, text):
- ctx = template.context.Context(context)
- return template.Template(text).render(ctx)
-
-
-def _build_url(request, obj_or_url):
- if obj_or_url is not None:
- if isinstance(obj_or_url, Model):
- return request.build_absolute_uri(obj_or_url.get_absolute_url())
- else:
- return request.build_absolute_uri(obj_or_url)
- return ''
-
-
-def _compose_tweet(text, url=None):
- TWITTER_MAX_NUMBER_OF_CHARACTERS = 140
- TWITTER_LINK_LENGTH = 23 # "A URL of any length will be altered to 23 characters, even if the link itself is less than 23 characters long.
-
- # Compute length of the tweet
- url_length = len(' ') + TWITTER_LINK_LENGTH if url else 0
- total_length = len(text) + url_length
-
- # Check that the text respects the max number of characters for a tweet
- if total_length > TWITTER_MAX_NUMBER_OF_CHARACTERS:
- text = text[:(TWITTER_MAX_NUMBER_OF_CHARACTERS - url_length - 1)] + "…" # len("…") == 1
-
- return "%s %s" % (text, url) if url else text
-
-
-@register.simple_tag(takes_context=True)
-def post_to_twitter_url(context, text, obj_or_url=None):
- text = compile_text(context, text)
- request = context['request']
-
- url = _build_url(request, obj_or_url)
-
- tweet = _compose_tweet(text, url)
- context['tweet_url'] = TWITTER_ENDPOINT % urlencode(tweet)
- return context
-
-
-@register.inclusion_tag('django_social_share/templatetags/post_to_twitter.html', takes_context=True)
-def post_to_twitter(context, text, obj_or_url=None, link_text='',link_class=""):
- context = post_to_twitter_url(context, text, obj_or_url)
-
- request = context['request']
- url = _build_url(request, obj_or_url)
- tweet = _compose_tweet(text, url)
-
- context['link_class'] = link_class
- context['link_text'] = link_text or 'Post to Twitter'
- context['full_text'] = tweet
- return context
-
-
-@register.simple_tag(takes_context=True)
-def post_to_facebook_url(context, obj_or_url=None):
- request = context['request']
- url = _build_url(request, obj_or_url)
- context['facebook_url'] = FACEBOOK_ENDPOINT % urlencode(url)
- return context
-
-
-@register.inclusion_tag('django_social_share/templatetags/post_to_facebook.html', takes_context=True)
-def post_to_facebook(context, obj_or_url=None, link_text='',link_class=''):
- context = post_to_facebook_url(context, obj_or_url)
- context['link_class'] = link_class or ''
- context['link_text'] = link_text or 'Post to Facebook'
- return context
-
-
-@register.simple_tag(takes_context=True)
-def post_to_gplus_url(context, obj_or_url=None):
- request = context['request']
- url = _build_url(request, obj_or_url)
- context['gplus_url'] = GPLUS_ENDPOINT % urlencode(url)
- return context
-
-
-@register.inclusion_tag('django_social_share/templatetags/post_to_gplus.html', takes_context=True)
-def post_to_gplus(context, obj_or_url=None, link_text='',link_class=''):
- context = post_to_gplus_url(context, obj_or_url)
- context['link_class'] = link_class
- context['link_text'] = link_text or 'Post to Google+'
- return context
-
-
-@register.simple_tag(takes_context=True)
-def send_email_url(context, subject, text, obj_or_url=None):
- text = compile_text(context, text)
- subject = compile_text(context, subject)
- request = context['request']
- url = _build_url(request, obj_or_url)
- full_text = "%s %s" % (text, url)
- context['mailto_url'] = MAIL_ENDPOINT % (urlencode(subject), urlencode(full_text))
- return context
-
-
-@register.inclusion_tag('django_social_share/templatetags/send_email.html', takes_context=True)
-def send_email(context, subject, text, obj_or_url=None, link_text='',link_class=''):
- context = send_email_url(context, subject, text, obj_or_url)
- context['link_class'] = link_class
- context['link_text'] = link_text or 'Share via email'
- return context
-
-
-@register.filter(name='linkedin_locale')
-def linkedin_locale(value):
- if "-" not in value:
- return value
-
- lang, country = value.split('-')
- return '_'.join([lang, country.upper()])
-
-
-@register.simple_tag(takes_context=True)
-def post_to_linkedin_url(context, obj_or_url=None):
- request = context['request']
- url = _build_url(request, obj_or_url)
- context['linkedin_url'] = LINKEDIN_ENDPOINT % urlencode(url)
- return context
-
-
-@register.inclusion_tag('django_social_share/templatetags/post_to_linkedin.html', takes_context=True)
-def post_to_linkedin(context, obj_or_url=None,link_class=''):
- context = post_to_linkedin_url(context, obj_or_url)
- context['link_class'] = link_class
- return context
-
-
-@register.simple_tag(takes_context=True)
-def post_to_reddit_url(context, title, obj_or_url=None):
- request = context['request']
- title = compile_text(context, title)
- url = _build_url(request, obj_or_url)
- context['reddit_url'] = mark_safe(REDDIT_ENDPOINT % (urlencode(title), urlencode(url)))
- return context
-
-
-@register.inclusion_tag('django_social_share/templatetags/post_to_reddit.html', takes_context=True)
-def post_to_reddit(context, title, obj_or_url=None, link_text='',link_class=''):
- context = post_to_reddit_url(context, title, obj_or_url)
- context['link_class'] = link_class
- context['link_text'] = link_text or 'Post to Reddit'
- return context
-
-
-@register.simple_tag(takes_context=True)
-def post_to_telegram_url(context, title, obj_or_url):
- request = context['request']
- title = compile_text(context, title)
- url = _build_url(request, obj_or_url)
- context['telegram_url'] = mark_safe(TELEGRAM_ENDPOINT % (urlencode(title), urlencode(url)))
- return context
-
-
-@register.inclusion_tag('django_social_share/templatetags/post_to_telegram.html', takes_context=True)
-def post_to_telegram(context, title, obj_or_url=None, link_text='',link_class=''):
- context = post_to_telegram_url(context, title, obj_or_url)
- context['link_class'] = link_class
- context['link_text'] = link_text or 'Post to Telegram'
- return context
-
-
-@register.simple_tag(takes_context=True)
-def post_to_whatsapp_url(context, obj_or_url=None):
- request = context['request']
- url = _build_url(request, obj_or_url)
- context['whatsapp_url'] = WHATSAPP_ENDPOINT % urlencode(url)
- return context
-
-
-@register.inclusion_tag('django_social_share/templatetags/post_to_whatsapp.html', takes_context=True)
-def post_to_whatsapp(context, obj_or_url=None, link_text='',link_class=''):
- context = post_to_whatsapp_url(context, obj_or_url)
- context['link_class'] = link_class
- context['link_text'] = link_text or 'Post to WhatsApp'
- return context
-
-
-@register.simple_tag(takes_context=True)
-def save_to_pinterest_url(context, obj_or_url=None):
- request = context['request']
- url = _build_url(request, obj_or_url)
- context['pinterest_url'] = PINTEREST_ENDPOINT % urlencode(url)
- return context
-
-
-@register.inclusion_tag('django_social_share/templatetags/save_to_pinterest.html', takes_context=True)
-def save_to_pinterest(context, obj_or_url=None, pin_count=False, link_class=''):
- context = save_to_pinterest_url(context, obj_or_url)
- context['link_class'] = link_class
- context['pin_count'] = pin_count
- return context
-
-
-@register.inclusion_tag('django_social_share/templatetags/pinterest_script.html', takes_context=False)
-def add_pinterest_script():
- pass
-
-@register.simple_tag(takes_context=True)
-def copy_to_clipboard_url(context, obj_or_url=None):
- request = context['request']
- url = _build_url(request, obj_or_url)
- context['copy_url'] = url
- return context
-
-@register.inclusion_tag('django_social_share/templatetags/copy_to_clipboard.html', takes_context=True)
-def copy_to_clipboard(context, obj_or_url, link_text='', link_class=''):
- context = copy_to_clipboard_url(context, obj_or_url)
-
- context['link_class'] = link_class
- context['link_text'] = link_text or 'Copy to clipboard'
- return context
-
-@register.inclusion_tag('django_social_share/templatetags/copy_script.html', takes_context=False)
-def add_copy_script():
- pass
\ No newline at end of file
diff --git a/qgisfeedproject/qgisfeed/tests/test_feed_detail.py b/qgisfeedproject/qgisfeed/tests/test_feed_detail.py
new file mode 100644
index 0000000..24ee25d
--- /dev/null
+++ b/qgisfeedproject/qgisfeed/tests/test_feed_detail.py
@@ -0,0 +1,55 @@
+# tests.py
+from django.test import TestCase, RequestFactory
+from django.urls import reverse
+from django.contrib.auth.models import User
+from django.utils import timezone
+from django.shortcuts import get_object_or_404
+from ..models import QgisFeedEntry
+from ..views import FeedEntryDetailView
+from django.contrib.gis.geos import Polygon
+from django.conf import settings
+from django.test import Client
+from os.path import join
+
+class FeedEntryDetailViewTestCase(TestCase):
+ """
+ Test feed detail page web version
+ """
+ fixtures = ['qgisfeed.json', 'users.json']
+ def setUp(self):
+ self.client = Client()
+ spatial_filter = Polygon(((0, 0), (0, 1), (1, 1), (1, 0), (0, 0)))
+ image_path = join(settings.MEDIA_ROOT, "feedimages", "rust.png")
+ self.post_data = {
+ 'title': 'QGIS core will be rewritten in Rust',
+ 'image': open(image_path, "rb"),
+ 'content': '
Tired with C++ intricacies, the core developers have decided to rewrite QGIS in Rust',
+ 'url': 'https://www.null.com',
+ 'sticky': False,
+ 'sorting': 0,
+ 'language_filter': 'en',
+ 'spatial_filter': str(spatial_filter),
+ 'publish_from': '2023-10-18 14:46:00+00',
+ 'publish_to': '2023-10-29 14:46:00+00'
+ }
+ # Add a feed entry test
+ self.client.login(username='staff', password='staff')
+
+ self.client.post(reverse('feed_entry_add'), data=self.post_data)
+
+ self.entry = QgisFeedEntry.objects.last()
+
+ self.factory = RequestFactory()
+
+ def test_feed_entry_detail_view(self):
+ url = reverse('feed_entry_detail', kwargs={'pk': self.entry.pk})
+ response = self.client.get(url)
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'feeds/feed_item_detail.html')
+ self.assertEqual(response.context['feed_entry'], self.entry)
+ self.assertIsNotNone(response.context['spatial_filter_geojson'])
+
+ def test_feed_entry_detail_view_not_found(self):
+ url = reverse('feed_entry_detail', kwargs={'pk': 999})
+ response = self.client.get(url)
+ self.assertEqual(response.status_code, 404)
diff --git a/qgisfeedproject/qgisfeedproject/settings.py b/qgisfeedproject/qgisfeedproject/settings.py
index 695b0ec..050a4ab 100644
--- a/qgisfeedproject/qgisfeedproject/settings.py
+++ b/qgisfeedproject/qgisfeedproject/settings.py
@@ -53,7 +53,10 @@
'user_visit',
# Webpack
- 'webpack_loader'
+ 'webpack_loader',
+
+ # Social share
+ 'django_social_share'
]
# Useful debugging extensions
diff --git a/qgisfeedproject/templates/django_social_share/templatetags/post_to_linkedin.html b/qgisfeedproject/templates/django_social_share/templatetags/post_to_linkedin.html
index 2be5182..b3699df 100644
--- a/qgisfeedproject/templates/django_social_share/templatetags/post_to_linkedin.html
+++ b/qgisfeedproject/templates/django_social_share/templatetags/post_to_linkedin.html
@@ -1,5 +1,5 @@
From cd95194b70d40ce9e22131351a388a0642451c81 Mon Sep 17 00:00:00 2001
From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com>
Date: Wed, 19 Jun 2024 10:23:01 +0300
Subject: [PATCH 7/8] Remove unused functions
---
qgisfeedproject/qgisfeed/utils.py | 50 -------------------------------
1 file changed, 50 deletions(-)
diff --git a/qgisfeedproject/qgisfeed/utils.py b/qgisfeedproject/qgisfeed/utils.py
index a518016..28aaf29 100644
--- a/qgisfeedproject/qgisfeed/utils.py
+++ b/qgisfeedproject/qgisfeed/utils.py
@@ -44,53 +44,3 @@ def get_field_max_length(ConfigurationModel: Model, field_name: str):
return config.max_characters
except ConfigurationModel.DoesNotExist:
return 500
-
-
-def push_to_linkedin(title, content):
- access_token = 'YOUR_ACCESS_TOKEN'
- headers = {
- 'Authorization': f'Bearer {access_token}',
- 'Content-Type': 'application/json',
- 'X-Restli-Protocol-Version': '2.0.0'
- }
- payload = {
- "author": "urn:li:person:YOUR_PERSON_URN",
- "lifecycleState": "PUBLISHED",
- "specificContent": {
- "com.linkedin.ugc.ShareContent": {
- "shareCommentary": {
- "text": title + content
- },
- "shareMediaCategory": "NONE"
- }
- },
- "visibility": {
- "com.linkedin.ugc.MemberNetworkVisibility": "PUBLIC"
- }
- }
- response = requests.post('https://api.linkedin.com/v2/ugcPosts', headers=headers, json=payload)
- return response.json()
-
-
-def push_to_facebook(title, content):
- access_token = 'YOUR_ACCESS_TOKEN'
- page_id = 'YOUR_PAGE_ID'
- url = f'https://graph.facebook.com/{page_id}/feed'
- payload = {
- 'message': title + content,
- 'access_token': access_token
- }
- response = requests.post(url, data=payload)
- return response.json()
-
-
-def push_to_telegram(title, content):
- bot_token = 'YOUR_BOT_TOKEN'
- chat_id = 'YOUR_GROUP_CHAT_ID'
- url = f'https://api.telegram.org/bot{bot_token}/sendMessage'
- payload = {
- 'chat_id': chat_id,
- 'text': title + content
- }
- response = requests.post(url, data=payload)
- return response.json()
From bc0846d9141b3543493471af38727bc8cba0ec43 Mon Sep 17 00:00:00 2001
From: Lova ANDRIARIMALALA <43842786+Xpirix@users.noreply.github.com>
Date: Wed, 19 Jun 2024 10:31:02 +0300
Subject: [PATCH 8/8] Remove unused imports in utils.py
---
qgisfeedproject/qgisfeed/utils.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/qgisfeedproject/qgisfeed/utils.py b/qgisfeedproject/qgisfeed/utils.py
index 28aaf29..f3a1278 100644
--- a/qgisfeedproject/qgisfeed/utils.py
+++ b/qgisfeedproject/qgisfeed/utils.py
@@ -5,9 +5,7 @@
from django.urls import reverse
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
-from django.core.mail import send_mail
from django.contrib.gis.db.models import Model
-import requests
logger = logging.getLogger('qgisfeed.admin')
QGISFEED_FROM_EMAIL = getattr(settings, 'QGISFEED_FROM_EMAIL', 'noreply@qgis.org')