Skip to content

Commit eb6f161

Browse files
committed
Add maintainer and display_created_by fields
1 parent 7527c5b commit eb6f161

7 files changed

+193
-4
lines changed

qgis-app/plugins/forms.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ class Meta:
4343
"tracker",
4444
"repository",
4545
"owners",
46-
"created_by",
46+
"maintainer",
47+
"display_created_by",
4748
"tags",
4849
"server",
4950
)
@@ -58,8 +59,8 @@ def __init__(self, *args, **kwargs):
5859
for owner in self.instance.owners.exclude(pk=self.instance.created_by.pk):
5960
choices += ((owner.pk, owner.username + " (Collaborator)"),)
6061

61-
self.fields['created_by'].choices = choices
62-
self.fields['created_by'].label = "Maintainer"
62+
self.fields['maintainer'].choices = choices
63+
self.fields['maintainer'].label = "Maintainer"
6364

6465
def clean(self):
6566
"""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Generated by Django 2.2.25 on 2023-11-23 07:56
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('plugins', '0002_plugins_feedback'),
10+
('plugins', '0003_plugin_allow_update_name'),
11+
]
12+
13+
operations = [
14+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Generated by Django 2.2.25 on 2023-11-29 22:45
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
def populate_maintainer(apps, schema_editor):
8+
Plugin = apps.get_model('plugins', 'Plugin')
9+
10+
# Set the maintainer as the plugin creator by default
11+
for obj in Plugin.objects.all():
12+
obj.maintainer = obj.created_by
13+
obj.save()
14+
15+
class Migration(migrations.Migration):
16+
17+
dependencies = [
18+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
19+
('plugins', '0004_merge_20231122_0223'),
20+
]
21+
22+
operations = [
23+
migrations.AddField(
24+
model_name='plugin',
25+
name='maintainer',
26+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='plugins_maintainer', to=settings.AUTH_USER_MODEL, verbose_name='Maintainer'),
27+
),
28+
migrations.RunPython(populate_maintainer),
29+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 2.2.25 on 2023-11-29 23:22
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('plugins', '0005_plugin_maintainer'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='plugin',
15+
name='display_created_by',
16+
field=models.BooleanField(default=False, verbose_name='Display "Created by" in plugin details'),
17+
),
18+
]

qgis-app/plugins/models.py

+19
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,22 @@ class Plugin(models.Model):
322322
related_name="plugins_created_by",
323323
on_delete=models.CASCADE,
324324
)
325+
326+
# maintainer
327+
maintainer = models.ForeignKey(
328+
User,
329+
verbose_name=_("Maintainer"),
330+
related_name="plugins_maintainer",
331+
on_delete=models.CASCADE,
332+
blank=True,
333+
null=True
334+
)
335+
336+
display_created_by = models.BooleanField(
337+
_('Display "Created by" in plugin details'),
338+
default=False
339+
)
340+
325341
author = models.CharField(
326342
_("Author"),
327343
help_text=_(
@@ -529,6 +545,7 @@ def save(self, keep_date=False, *args, **kwargs):
529545
"""
530546
Soft triggers:
531547
* updates modified_on if keep_date is not set
548+
* set maintainer to the plugin creator when not specified
532549
"""
533550
if self.pk and not keep_date:
534551
import logging
@@ -537,6 +554,8 @@ def save(self, keep_date=False, *args, **kwargs):
537554
self.modified_on = datetime.datetime.now()
538555
if not self.pk:
539556
self.modified_on = datetime.datetime.now()
557+
if not self.maintainer:
558+
self.maintainer = self.created_by
540559
super(Plugin, self).save(*args, **kwargs)
541560

542561

qgis-app/plugins/templates/plugins/plugin_detail.html

+8-1
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,16 @@ <h2>{{ object.name }}
126126
<dt>{% trans "Author's email"%}</dt>
127127
<dd> <a href="mailto:{{ object.email }}">{{ object.email }}</a></dd>
128128
{% endif %}
129+
{% if object.display_created_by %}
130+
<dt>{% trans "Created by"%}</dt>
131+
<dd>
132+
<a href="{% url "user_details" object.created_by %}">{{ object.created_by }}</a>
133+
</dd>
134+
135+
{% endif %}
129136
<dt>{% trans "Maintainer"%}</dt>
130137
<dd>
131-
<a href="{% url "user_details" object.created_by %}">{{ object.created_by }}</a>
138+
<a href="{% url "user_details" object.maintainer %}">{{ object.maintainer }}</a>
132139
</dd>
133140
{% if object.owners.count %}
134141
<dt>{% trans "Collaborators"%}</dt>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import os
2+
from unittest.mock import patch
3+
4+
from django.urls import reverse
5+
from django.test import Client, TestCase, override_settings
6+
from django.contrib.auth.models import User
7+
from django.core.files.uploadedfile import SimpleUploadedFile
8+
from plugins.models import Plugin, PluginVersion
9+
from plugins.forms import PluginForm
10+
11+
def do_nothing(*args, **kwargs):
12+
pass
13+
14+
TESTFILE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "testfiles"))
15+
16+
class PluginRenameTestCase(TestCase):
17+
fixtures = [
18+
"fixtures/styles.json",
19+
"fixtures/auth.json",
20+
"fixtures/simplemenu.json",
21+
]
22+
23+
@override_settings(MEDIA_ROOT="api/tests")
24+
def setUp(self):
25+
self.client = Client()
26+
self.url_upload = reverse('plugin_upload')
27+
28+
# Create a test user
29+
self.user = User.objects.create_user(
30+
username='testuser',
31+
password='testpassword',
32+
33+
)
34+
35+
# Log in the test user
36+
self.client.login(username='testuser', password='testpassword')
37+
38+
# Upload a plugin for renaming test.
39+
# This process is already tested in test_plugin_upload
40+
valid_plugin = os.path.join(TESTFILE_DIR, "valid_plugin.zip_")
41+
with open(valid_plugin, "rb") as file:
42+
uploaded_file = SimpleUploadedFile(
43+
"valid_plugin.zip_", file.read(),
44+
content_type="application/zip")
45+
46+
self.client.post(self.url_upload, {
47+
'package': uploaded_file,
48+
})
49+
50+
self.plugin = Plugin.objects.get(name='Test Plugin')
51+
self.plugin.save()
52+
53+
@patch("plugins.tasks.generate_plugins_xml.delay", new=do_nothing)
54+
@patch("plugins.validator._check_url_link", new=do_nothing)
55+
def test_change_maintainer(self):
56+
"""
57+
Test change maintainer for plugin update
58+
"""
59+
package_name = self.plugin.package_name
60+
self.url_plugin_update = reverse('plugin_update', args=[package_name])
61+
self.url_add_version = reverse('version_create', args=[package_name])
62+
63+
# Test GET request
64+
response = self.client.get(self.url_plugin_update)
65+
self.assertEqual(response.status_code, 200)
66+
self.assertIsInstance(response.context['form'], PluginForm)
67+
self.assertEqual(response.context['form']['maintainer'].value(), self.user.pk)
68+
69+
70+
# Test POST request to change maintainer
71+
72+
response = self.client.post(self.url_plugin_update, {
73+
'description': self.plugin.description,
74+
'about': self.plugin.about,
75+
'author': self.plugin.author,
76+
'email': self.plugin.email,
77+
'tracker': self.plugin.tracker,
78+
'repository': self.plugin.repository,
79+
'maintainer': 1,
80+
})
81+
self.assertEqual(response.status_code, 302)
82+
self.assertEqual(Plugin.objects.get(name='Test Plugin').maintainer.pk, 1)
83+
84+
# Test POST request with new version
85+
86+
valid_plugin = os.path.join(TESTFILE_DIR, "valid_plugin_0.0.2.zip_")
87+
with open(valid_plugin, "rb") as file:
88+
uploaded_file = SimpleUploadedFile(
89+
"valid_plugin_0.0.2.zip_", file.read(),
90+
content_type="application/zip_")
91+
92+
response = self.client.post(self.url_add_version, {
93+
'package': uploaded_file,
94+
'experimental': False,
95+
'changelog': ''
96+
})
97+
self.assertEqual(response.status_code, 302)
98+
self.assertEqual(Plugin.objects.get(name='Test Plugin').maintainer.pk, 1)
99+
100+
def tearDown(self):
101+
self.client.logout()

0 commit comments

Comments
 (0)