Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add maintainer field to plugin update #316

Merged
merged 3 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions qgis-app/plugins/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,25 @@ class Meta:
"tracker",
"repository",
"owners",
"maintainer",
"display_created_by",
"tags",
"server",
)

def __init__(self, *args, **kwargs):
super(PluginForm, self).__init__(*args, **kwargs)
self.fields['owners'].label = "Collaborators"

choices = (
(self.instance.created_by.pk, self.instance.created_by.username + " (Plugin creator)"),
)
for owner in self.instance.owners.exclude(pk=self.instance.created_by.pk):
choices += ((owner.pk, owner.username + " (Collaborator)"),)

self.fields['maintainer'].choices = choices
self.fields['maintainer'].label = "Maintainer"

def clean(self):
"""
Check author
Expand Down
29 changes: 29 additions & 0 deletions qgis-app/plugins/migrations/0005_plugin_maintainer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Generated by Django 2.2.25 on 2023-11-29 22:45

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion

def populate_maintainer(apps, schema_editor):
Plugin = apps.get_model('plugins', 'Plugin')

# Set the maintainer as the plugin creator by default
for obj in Plugin.objects.all():
obj.maintainer = obj.created_by
obj.save()

class Migration(migrations.Migration):

dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('plugins', '0004_merge_20231122_0223'),
]

operations = [
migrations.AddField(
model_name='plugin',
name='maintainer',
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'),
),
migrations.RunPython(populate_maintainer),
]
18 changes: 18 additions & 0 deletions qgis-app/plugins/migrations/0006_plugin_display_created_by.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.2.25 on 2023-11-29 23:22

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('plugins', '0005_plugin_maintainer'),
]

operations = [
migrations.AddField(
model_name='plugin',
name='display_created_by',
field=models.BooleanField(default=False, verbose_name='Display "Created by" in plugin details'),
),
]
19 changes: 19 additions & 0 deletions qgis-app/plugins/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,22 @@ class Plugin(models.Model):
related_name="plugins_created_by",
on_delete=models.CASCADE,
)

# maintainer
maintainer = models.ForeignKey(
User,
verbose_name=_("Maintainer"),
related_name="plugins_maintainer",
on_delete=models.CASCADE,
blank=True,
null=True
)

display_created_by = models.BooleanField(
_('Display "Created by" in plugin details'),
default=False
)

author = models.CharField(
_("Author"),
help_text=_(
Expand Down Expand Up @@ -529,6 +545,7 @@ def save(self, keep_date=False, *args, **kwargs):
"""
Soft triggers:
* updates modified_on if keep_date is not set
* set maintainer to the plugin creator when not specified
"""
if self.pk and not keep_date:
import logging
Expand All @@ -537,6 +554,8 @@ def save(self, keep_date=False, *args, **kwargs):
self.modified_on = datetime.datetime.now()
if not self.pk:
self.modified_on = datetime.datetime.now()
if not self.maintainer:
self.maintainer = self.created_by
super(Plugin, self).save(*args, **kwargs)


Expand Down
9 changes: 8 additions & 1 deletion qgis-app/plugins/templates/plugins/plugin_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,16 @@ <h2>{{ object.name }}
<dt>{% trans "Author's email"%}</dt>
<dd> <a href="mailto:{{ object.email }}">{{ object.email }}</a></dd>
{% endif %}
{% if object.display_created_by %}
<dt>{% trans "Created by"%}</dt>
<dd>
<a href="{% url "user_details" object.created_by %}">{{ object.created_by }}</a>
</dd>

{% endif %}
<dt>{% trans "Maintainer"%}</dt>
<dd>
<a href="{% url "user_details" object.created_by %}">{{ object.created_by }}</a>
<a href="{% url "user_details" object.maintainer %}">{{ object.maintainer }}</a>
</dd>
{% if object.owners.count %}
<dt>{% trans "Collaborators"%}</dt>
Expand Down
2 changes: 1 addition & 1 deletion qgis-app/plugins/templates/plugins/plugin_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ <h2>{{ form_title }} {{ plugin }}</h2>
let element = document.getElementById('id_owners');
if(element) {
$('#id_owners').chosen({
placeholder_text_multiple: "Select Some Owners",
placeholder_text_multiple: "Select Some Collaborators",
no_results_text: "Oops, nothing found!"
});
clearInterval(checkElement);
Expand Down
101 changes: 101 additions & 0 deletions qgis-app/plugins/tests/test_change_maintainer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import os
from unittest.mock import patch

from django.urls import reverse
from django.test import Client, TestCase, override_settings
from django.contrib.auth.models import User
from django.core.files.uploadedfile import SimpleUploadedFile
from plugins.models import Plugin, PluginVersion
from plugins.forms import PluginForm

def do_nothing(*args, **kwargs):
pass

TESTFILE_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "testfiles"))

class PluginRenameTestCase(TestCase):
fixtures = [
"fixtures/styles.json",
"fixtures/auth.json",
"fixtures/simplemenu.json",
]

@override_settings(MEDIA_ROOT="api/tests")
def setUp(self):
self.client = Client()
self.url_upload = reverse('plugin_upload')

# Create a test user
self.user = User.objects.create_user(
username='testuser',
password='testpassword',
email='[email protected]'
)

# Log in the test user
self.client.login(username='testuser', password='testpassword')

# Upload a plugin for renaming test.
# This process is already tested in test_plugin_upload
valid_plugin = os.path.join(TESTFILE_DIR, "valid_plugin.zip_")
with open(valid_plugin, "rb") as file:
uploaded_file = SimpleUploadedFile(
"valid_plugin.zip_", file.read(),
content_type="application/zip")

self.client.post(self.url_upload, {
'package': uploaded_file,
})

self.plugin = Plugin.objects.get(name='Test Plugin')
self.plugin.save()

@patch("plugins.tasks.generate_plugins_xml.delay", new=do_nothing)
@patch("plugins.validator._check_url_link", new=do_nothing)
def test_change_maintainer(self):
"""
Test change maintainer for plugin update
"""
package_name = self.plugin.package_name
self.url_plugin_update = reverse('plugin_update', args=[package_name])
self.url_add_version = reverse('version_create', args=[package_name])

# Test GET request
response = self.client.get(self.url_plugin_update)
self.assertEqual(response.status_code, 200)
self.assertIsInstance(response.context['form'], PluginForm)
self.assertEqual(response.context['form']['maintainer'].value(), self.user.pk)


# Test POST request to change maintainer

response = self.client.post(self.url_plugin_update, {
'description': self.plugin.description,
'about': self.plugin.about,
'author': self.plugin.author,
'email': self.plugin.email,
'tracker': self.plugin.tracker,
'repository': self.plugin.repository,
'maintainer': 1,
})
self.assertEqual(response.status_code, 302)
self.assertEqual(Plugin.objects.get(name='Test Plugin').maintainer.pk, 1)

# Test POST request with new version

valid_plugin = os.path.join(TESTFILE_DIR, "valid_plugin_0.0.2.zip_")
with open(valid_plugin, "rb") as file:
uploaded_file = SimpleUploadedFile(
"valid_plugin_0.0.2.zip_", file.read(),
content_type="application/zip_")

response = self.client.post(self.url_add_version, {
'package': uploaded_file,
'experimental': False,
'changelog': ''
})
self.assertEqual(response.status_code, 302)
self.assertEqual(Plugin.objects.get(name='Test Plugin').maintainer.pk, 1)

def tearDown(self):
self.client.logout()
Loading