From ed734ed949af9a4ea82333ba4e1133bd67487c8c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Corentin=20S=C3=A9chet?= <csechet@entrouvert.com>
Date: Tue, 29 Mar 2022 16:24:57 +0200
Subject: [PATCH] applications: add link to hide applications (#63273)

---
 .../migrations/0002_application_hidden.py     | 18 ++++++++++
 hobo/applications/models.py                   |  1 +
 .../hobo/applications/app_confirm_delete.html | 19 +++++++++++
 hobo/applications/urls.py                     |  1 +
 hobo/applications/views.py                    | 20 ++++++++++-
 tests/test_application.py                     | 34 +++++++++++++++++++
 6 files changed, 92 insertions(+), 1 deletion(-)
 create mode 100644 hobo/applications/migrations/0002_application_hidden.py
 create mode 100644 hobo/applications/templates/hobo/applications/app_confirm_delete.html

diff --git a/hobo/applications/migrations/0002_application_hidden.py b/hobo/applications/migrations/0002_application_hidden.py
new file mode 100644
index 0000000..c64d7c1
--- /dev/null
+++ b/hobo/applications/migrations/0002_application_hidden.py
@@ -0,0 +1,18 @@
+# Generated by Django 2.2.24 on 2022-03-29 10:00
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('applications', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='application',
+            name='hidden',
+            field=models.BooleanField(default=False),
+        ),
+    ]
diff --git a/hobo/applications/models.py b/hobo/applications/models.py
index f4bc094..545427a 100644
--- a/hobo/applications/models.py
+++ b/hobo/applications/models.py
@@ -40,6 +40,7 @@ class Application(models.Model):
     editable = models.BooleanField(default=True)
     elements = models.ManyToManyField('Element', blank=True, through='Relation')
     creation_timestamp = models.DateTimeField(default=now)
+    hidden = models.BooleanField(default=False)
     last_update_timestamp = models.DateTimeField(auto_now=True)
 
     def __repr__(self):
diff --git a/hobo/applications/templates/hobo/applications/app_confirm_delete.html b/hobo/applications/templates/hobo/applications/app_confirm_delete.html
new file mode 100644
index 0000000..aca0255
--- /dev/null
+++ b/hobo/applications/templates/hobo/applications/app_confirm_delete.html
@@ -0,0 +1,19 @@
+{% extends "hobo/base.html" %}
+{% load i18n %}
+
+{% block appbar %}
+<h2>{% blocktrans with title=object.name %}Removal of "{{ title }}"{% endblocktrans %}</h2>
+{% endblock %}
+
+{% block content %}
+<form method="post">
+  {% csrf_token %}
+  <p>
+  {% trans 'Are you sure you want to remove this application ?' %}
+  </p>
+  <div class="buttons">
+    <button class="delete-button">{% trans 'Delete' %}</button>
+    <a class="cancel" href="{% url 'application-manifest' app_slug=view.kwargs.slug %}">{% trans 'Cancel' %}</a>
+  </div>
+</form>
+{% endblock %}
diff --git a/hobo/applications/urls.py b/hobo/applications/urls.py
index ff62091..51eb09c 100644
--- a/hobo/applications/urls.py
+++ b/hobo/applications/urls.py
@@ -22,6 +22,7 @@ urlpatterns = [
     url(r'^$', views.home, name='applications-home'),
     url(r'^create/$', views.init, name='application-init'),
     url(r'^install/$', views.install, name='application-install'),
+    url(r'^manifest/(?P<slug>[\w-]+)/delete/$', views.delete, name='application-delete'),
     url(r'^manifest/(?P<app_slug>[\w-]+)/$', views.manifest, name='application-manifest'),
     url(r'^manifest/(?P<app_slug>[\w-]+)/metadata/$', views.metadata, name='application-metadata'),
     url(r'^manifest/(?P<app_slug>[\w-]+)/scandeps/$', views.scandeps, name='application-scandeps'),
diff --git a/hobo/applications/views.py b/hobo/applications/views.py
index d7d1e2b..166e00b 100644
--- a/hobo/applications/views.py
+++ b/hobo/applications/views.py
@@ -40,7 +40,7 @@ class HomeView(ListView):
     model = Application
 
     def get_queryset(self):
-        return super().get_queryset().order_by('name')
+        return super().get_queryset().filter(hidden=False).order_by('name')
 
 
 home = HomeView.as_view()
@@ -298,3 +298,21 @@ class Install(FormView):
 
 
 install = Install.as_view()
+
+
+class AppDeleteView(DeleteView):
+    model = Application
+    template_name = 'hobo/applications/app_confirm_delete.html'
+
+    def post(self, request, *args, **kwargs):
+        self.object = self.get_object()
+        success_url = self.get_success_url()
+        self.object.hidden = True
+        self.object.save()
+        return HttpResponseRedirect(success_url)
+
+    def get_success_url(self):
+        return reverse('applications-home')
+
+
+delete = AppDeleteView.as_view()
diff --git a/tests/test_application.py b/tests/test_application.py
index 60f04b6..87449e7 100644
--- a/tests/test_application.py
+++ b/tests/test_application.py
@@ -178,6 +178,40 @@ def test_create_application(app, admin_user, settings):
         assert b'<carddef/>' in resp.content
 
 
+def test_delete_application(app, admin_user, settings):
+    Wcs.objects.create(base_url='https://wcs.example.invalid', slug='foobar', title='Foobar')
+
+    settings.KNOWN_SERVICES = {
+        'wcs': {
+            'foobar': {
+                'title': 'Foobar',
+                'url': 'https://wcs.example.invalid/',
+                'orig': 'example.org',
+                'secret': 'xxx',
+            }
+        }
+    }
+
+    login(app)
+
+    Application.objects.create(name='AppToDelete', slug='app_to_delete')
+    Application.objects.create(name='OtherApp', slug='other_app')
+
+    filtered_applications = Application.objects.filter(hidden=False)
+    assert filtered_applications.count() == 2
+
+    resp = app.get('/applications/manifest/app_to_delete/delete/')
+
+    resp = resp.forms[0].submit()
+    resp = resp.follow()
+
+    assert '/applications/' in resp
+    assert 'AppToDelete' not in resp.text
+
+    assert filtered_applications.count() == 1
+    assert filtered_applications[0].name == 'OtherApp'
+
+
 @pytest.fixture
 def app_bundle():
     tar_io = io.BytesIO()
-- 
2.35.1

