We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
only module can be removed from published project
**URL:** https://meinberlin-dev.liqd.net/dashboard/projects/lorem-ipsum/basic/
**user:** initiator
**expected behaviour:** when a project is published, at least one modle should always be added to the project, the initiator shouldn't be able to remove the last one 
**behaviour:** last added module can be removed, resulting ind strange project detail view ("Die Beteiligung ist aktuell nicht möglich. Sie startet am None. ") with an empty project without any participation.
**important screensize:**
**device & browser:** 
**Comment/Question:** @CarolingerSeilchenspringer I am not sure we decided it should be like that (or can you just not delete the last module and only delete it after it has been removed?), but I think this needs to be fixed. Do you agree?

Screenshot?


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py`
Content:
```
1 import json
2 from urllib.parse import unquote
3 
4 from django import template
5 
6 register = template.Library()
7 
8 
9 @register.simple_tag(takes_context=True)
10 def closed_accordeons(context, project_id):
11     request = context['request']
12     cookie = request.COOKIES.get('dashboard_projects_closed_accordeons', '[]')
13     ids = json.loads(unquote(cookie))
14     if project_id in ids:
15         ids.append(-1)
16     return ids
17 
18 
19 @register.filter
20 def is_publishable(project, project_progress):
21     """Check if project can be published.
22 
23     Required project details need to be filled in and at least one module
24     has to be published (added to the project).
25     """
26     return (project_progress['project_is_complete']
27             and project.published_modules.count() >= 1)
28 
29 
30 @register.filter
31 def has_unpublishable_modules(project):
32     """Check if modules can be removed from project.
33 
34     Modules can be removed if the project is not yet published and there is
35     another module published for (added to) the project.
36     """
37     return (project.is_draft
38             and project.published_modules.count() <= 1)
39 
40 
41 @register.filter
42 def project_nav_is_active(dashboard_menu_project):
43     """Check if the view is in the project dashboard nav."""
44     for item in dashboard_menu_project:
45         if item['is_active']:
46             return True
47     return False
48 
49 
50 @register.filter
51 def module_nav_is_active(dashboard_menu_modules):
52     """Check if the view is in the project dashboard nav."""
53     for module_menu in dashboard_menu_modules:
54         for item in module_menu['menu']:
55             if item['is_active']:
56                 return True
57     return False
58 
59 
60 @register.filter
61 def has_publishable_module(dashboard_menu_modules):
62     for module_menu in dashboard_menu_modules:
63         if module_menu['is_complete']:
64             return True
65     return False
66
```

Path: `meinberlin/apps/dashboard/views.py`
Content:
```
1 import json
2 from urllib import parse
3 
4 from django.apps import apps
5 from django.contrib import messages
6 from django.contrib.messages.views import SuccessMessageMixin
7 from django.http import HttpResponseRedirect
8 from django.urls import resolve
9 from django.urls import reverse
10 from django.utils.translation import ugettext_lazy as _
11 from django.views import generic
12 from django.views.generic.detail import SingleObjectMixin
13 
14 from adhocracy4.dashboard import mixins
15 from adhocracy4.dashboard import signals
16 from adhocracy4.dashboard import views as a4dashboard_views
17 from adhocracy4.dashboard.blueprints import get_blueprints
18 from adhocracy4.modules import models as module_models
19 from adhocracy4.phases import models as phase_models
20 from adhocracy4.projects import models as project_models
21 from adhocracy4.projects.mixins import ProjectMixin
22 from meinberlin.apps.dashboard.forms import DashboardProjectCreateForm
23 
24 
25 class ModuleBlueprintListView(ProjectMixin,
26                               mixins.DashboardBaseMixin,
27                               mixins.BlueprintMixin,
28                               generic.DetailView):
29     template_name = 'meinberlin_dashboard/module_blueprint_list_dashboard.html'
30     permission_required = 'a4projects.change_project'
31     model = project_models.Project
32     slug_url_kwarg = 'project_slug'
33     menu_item = 'project'
34 
35     @property
36     def blueprints(self):
37         return get_blueprints()
38 
39     def get_permission_object(self):
40         return self.project
41 
42 
43 class ModuleCreateView(ProjectMixin,
44                        mixins.DashboardBaseMixin,
45                        mixins.BlueprintMixin,
46                        SingleObjectMixin,
47                        generic.View):
48     permission_required = 'a4projects.change_project'
49     model = project_models.Project
50     slug_url_kwarg = 'project_slug'
51     success_message = _('The module was created')
52 
53     def post(self, request, *args, **kwargs):
54         project = self.get_object()
55         weight = 1
56         if project.modules:
57             weight = max(
58                 project.modules.values_list('weight', flat=True)
59             ) + 1
60         module = module_models.Module(
61             name=self.blueprint.title,
62             weight=weight,
63             project=project,
64             is_draft=True,
65         )
66         module.save()
67         signals.module_created.send(sender=None,
68                                     module=module,
69                                     user=self.request.user)
70 
71         self._create_module_settings(module)
72         self._create_phases(module, self.blueprint.content)
73         messages.success(self.request, self.success_message)
74 
75         cookie = request.COOKIES.get('dashboard_projects_closed_accordeons',
76                                      '[]')
77         ids = json.loads(parse.unquote(cookie))
78         if self.project.id not in ids:
79             ids.append(self.project.id)
80 
81         cookie = parse.quote(json.dumps(ids))
82 
83         response = HttpResponseRedirect(self.get_next(module))
84         response.set_cookie('dashboard_projects_closed_accordeons', cookie)
85         return response
86 
87     def _create_module_settings(self, module):
88         if self.blueprint.settings_model:
89             settings_model = apps.get_model(*self.blueprint.settings_model)
90             module_settings = settings_model(module=module)
91             module_settings.save()
92 
93     def _create_phases(self, module, blueprint_phases):
94         for index, phase_content in enumerate(blueprint_phases):
95             phase = phase_models.Phase(
96                 type=phase_content.identifier,
97                 name=phase_content.name,
98                 description=phase_content.description,
99                 weight=index,
100                 module=module,
101             )
102             phase.save()
103 
104     def get_next(self, module):
105         return reverse('a4dashboard:dashboard-module_basic-edit', kwargs={
106             'module_slug': module.slug
107         })
108 
109     def get_permission_object(self):
110         return self.project
111 
112 
113 class ModulePublishView(SingleObjectMixin,
114                         generic.View):
115     permission_required = 'a4projects.change_project'
116     model = module_models.Module
117     slug_url_kwarg = 'module_slug'
118 
119     def get_permission_object(self):
120         return self.get_object().project
121 
122     def post(self, request, *args, **kwargs):
123         action = request.POST.get('action', None)
124         if action == 'publish':
125             self.publish_module()
126         elif action == 'unpublish':
127             self.unpublish_module()
128         else:
129             messages.warning(self.request, _('Invalid action'))
130 
131         return HttpResponseRedirect(self.get_next())
132 
133     def get_next(self):
134         if 'referrer' in self.request.POST:
135             return self.request.POST['referrer']
136         elif 'HTTP_REFERER' in self.request.META:
137             return self.request.META['HTTP_REFERER']
138 
139         return reverse('a4dashboard:project-edit', kwargs={
140             'project_slug': self.project.slug
141         })
142 
143     def publish_module(self):
144         module = self.get_object()
145         if not module.is_draft:
146             messages.info(self.request, _('Module is already added'))
147             return
148 
149         module.is_draft = False
150         module.save()
151 
152         signals.module_published.send(sender=None,
153                                       module=module,
154                                       user=self.request.user)
155 
156         messages.success(self.request,
157                          _('The module is displayed in the project.'))
158 
159     def unpublish_module(self):
160         module = self.get_object()
161         if module.is_draft:
162             messages.info(self.request, _('Module is already removed'))
163             return
164 
165         module.is_draft = True
166         module.save()
167 
168         signals.module_unpublished.send(sender=None,
169                                         module=module,
170                                         user=self.request.user)
171 
172         messages.success(self.request,
173                          _('The module is no longer displayed in the project.'
174                            ))
175 
176 
177 class ModuleDeleteView(generic.DeleteView):
178     permission_required = 'a4projects.change_project'
179     model = module_models.Module
180     success_message = _('The module has been deleted')
181 
182     def delete(self, request, *args, **kwargs):
183         messages.success(self.request, self.success_message)
184         return super().delete(request, *args, **kwargs)
185 
186     def get_permission_object(self):
187         return self.get_object().project
188 
189     def get_success_url(self):
190         referrer = self.request.POST.get('referrer', None) \
191             or self.request.META.get('HTTP_REFERER', None)
192         if referrer:
193             view, args, kwargs = resolve(referrer)
194             if 'module_slug' not in kwargs \
195                     or not kwargs['module_slug'] == self.get_object().slug:
196                 return referrer
197 
198         return reverse('a4dashboard:project-edit', kwargs={
199             'project_slug': self.get_object().project.slug
200         })
201 
202 
203 class DashboardProjectListView(a4dashboard_views.ProjectListView):
204     def get_queryset(self):
205         return super().get_queryset().filter(
206             projectcontainer=None,
207             externalproject=None
208         )
209 
210 
211 class ProjectCreateView(mixins.DashboardBaseMixin,
212                         SuccessMessageMixin,
213                         generic.CreateView):
214     model = project_models.Project
215     slug_url_kwarg = 'project_slug'
216     form_class = DashboardProjectCreateForm
217     template_name = 'a4dashboard/project_create_form.html'
218     permission_required = 'a4projects.add_project'
219     menu_item = 'project'
220     success_message = _('Project was created.')
221 
222     def get_permission_object(self):
223         return self.organisation
224 
225     def get_form_kwargs(self):
226         kwargs = super().get_form_kwargs()
227         kwargs['organisation'] = self.organisation
228         kwargs['creator'] = self.request.user
229         return kwargs
230 
231     def get_success_url(self):
232         return reverse('a4dashboard:project-edit',
233                        kwargs={'project_slug': self.object.slug})
234 
235     def form_valid(self, form):
236         response = super().form_valid(form)
237         signals.project_created.send(sender=None,
238                                      project=self.object,
239                                      user=self.request.user)
240 
241         return response
242
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py b/meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py
--- a/meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py
+++ b/meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py
@@ -35,7 +35,7 @@
     another module published for (added to) the project.
     """
     return (project.is_draft
-            and project.published_modules.count() <= 1)
+            and project.published_modules.count() > 1)
 
 
 @register.filter
diff --git a/meinberlin/apps/dashboard/views.py b/meinberlin/apps/dashboard/views.py
--- a/meinberlin/apps/dashboard/views.py
+++ b/meinberlin/apps/dashboard/views.py
@@ -161,6 +161,16 @@
         if module.is_draft:
             messages.info(self.request, _('Module is already removed'))
             return
+        if not module.project.is_draft:
+            messages.error(self.request,
+                           _('Module cannot be removed '
+                             'from a published project.'))
+            return
+        if module.project.published_modules.count() == 1:
+            messages.error(self.request,
+                           _('Module cannot be removed. '
+                             'It is the only module added to the project.'))
+            return
 
         module.is_draft = True
         module.save()

{"golden_diff": "diff --git a/meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py b/meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py\n--- a/meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py\n+++ b/meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py\n@@ -35,7 +35,7 @@\n     another module published for (added to) the project.\n     \"\"\"\n     return (project.is_draft\n-            and project.published_modules.count() <= 1)\n+            and project.published_modules.count() > 1)\n \n \n @register.filter\ndiff --git a/meinberlin/apps/dashboard/views.py b/meinberlin/apps/dashboard/views.py\n--- a/meinberlin/apps/dashboard/views.py\n+++ b/meinberlin/apps/dashboard/views.py\n@@ -161,6 +161,16 @@\n         if module.is_draft:\n             messages.info(self.request, _('Module is already removed'))\n             return\n+        if not module.project.is_draft:\n+            messages.error(self.request,\n+                           _('Module cannot be removed '\n+                             'from a published project.'))\n+            return\n+        if module.project.published_modules.count() == 1:\n+            messages.error(self.request,\n+                           _('Module cannot be removed. '\n+                             'It is the only module added to the project.'))\n+            return\n \n         module.is_draft = True\n         module.save()\n", "issue": "only module can be removed from published project\n**URL:** https://meinberlin-dev.liqd.net/dashboard/projects/lorem-ipsum/basic/\r\n**user:** initiator\r\n**expected behaviour:** when a project is published, at least one modle should always be added to the project, the initiator shouldn't be able to remove the last one \r\n**behaviour:** last added module can be removed, resulting ind strange project detail view (\"Die Beteiligung ist aktuell nicht m\u00f6glich. Sie startet am None. \") with an empty project without any participation.\r\n**important screensize:**\r\n**device & browser:** \r\n**Comment/Question:** @CarolingerSeilchenspringer I am not sure we decided it should be like that (or can you just not delete the last module and only delete it after it has been removed?), but I think this needs to be fixed. Do you agree?\r\n\r\nScreenshot?\r\n\n", "before_files": [{"content": "import json\nfrom urllib.parse import unquote\n\nfrom django import template\n\nregister = template.Library()\n\n\n@register.simple_tag(takes_context=True)\ndef closed_accordeons(context, project_id):\n    request = context['request']\n    cookie = request.COOKIES.get('dashboard_projects_closed_accordeons', '[]')\n    ids = json.loads(unquote(cookie))\n    if project_id in ids:\n        ids.append(-1)\n    return ids\n\n\n@register.filter\ndef is_publishable(project, project_progress):\n    \"\"\"Check if project can be published.\n\n    Required project details need to be filled in and at least one module\n    has to be published (added to the project).\n    \"\"\"\n    return (project_progress['project_is_complete']\n            and project.published_modules.count() >= 1)\n\n\n@register.filter\ndef has_unpublishable_modules(project):\n    \"\"\"Check if modules can be removed from project.\n\n    Modules can be removed if the project is not yet published and there is\n    another module published for (added to) the project.\n    \"\"\"\n    return (project.is_draft\n            and project.published_modules.count() <= 1)\n\n\n@register.filter\ndef project_nav_is_active(dashboard_menu_project):\n    \"\"\"Check if the view is in the project dashboard nav.\"\"\"\n    for item in dashboard_menu_project:\n        if item['is_active']:\n            return True\n    return False\n\n\n@register.filter\ndef module_nav_is_active(dashboard_menu_modules):\n    \"\"\"Check if the view is in the project dashboard nav.\"\"\"\n    for module_menu in dashboard_menu_modules:\n        for item in module_menu['menu']:\n            if item['is_active']:\n                return True\n    return False\n\n\n@register.filter\ndef has_publishable_module(dashboard_menu_modules):\n    for module_menu in dashboard_menu_modules:\n        if module_menu['is_complete']:\n            return True\n    return False\n", "path": "meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py"}, {"content": "import json\nfrom urllib import parse\n\nfrom django.apps import apps\nfrom django.contrib import messages\nfrom django.contrib.messages.views import SuccessMessageMixin\nfrom django.http import HttpResponseRedirect\nfrom django.urls import resolve\nfrom django.urls import reverse\nfrom django.utils.translation import ugettext_lazy as _\nfrom django.views import generic\nfrom django.views.generic.detail import SingleObjectMixin\n\nfrom adhocracy4.dashboard import mixins\nfrom adhocracy4.dashboard import signals\nfrom adhocracy4.dashboard import views as a4dashboard_views\nfrom adhocracy4.dashboard.blueprints import get_blueprints\nfrom adhocracy4.modules import models as module_models\nfrom adhocracy4.phases import models as phase_models\nfrom adhocracy4.projects import models as project_models\nfrom adhocracy4.projects.mixins import ProjectMixin\nfrom meinberlin.apps.dashboard.forms import DashboardProjectCreateForm\n\n\nclass ModuleBlueprintListView(ProjectMixin,\n                              mixins.DashboardBaseMixin,\n                              mixins.BlueprintMixin,\n                              generic.DetailView):\n    template_name = 'meinberlin_dashboard/module_blueprint_list_dashboard.html'\n    permission_required = 'a4projects.change_project'\n    model = project_models.Project\n    slug_url_kwarg = 'project_slug'\n    menu_item = 'project'\n\n    @property\n    def blueprints(self):\n        return get_blueprints()\n\n    def get_permission_object(self):\n        return self.project\n\n\nclass ModuleCreateView(ProjectMixin,\n                       mixins.DashboardBaseMixin,\n                       mixins.BlueprintMixin,\n                       SingleObjectMixin,\n                       generic.View):\n    permission_required = 'a4projects.change_project'\n    model = project_models.Project\n    slug_url_kwarg = 'project_slug'\n    success_message = _('The module was created')\n\n    def post(self, request, *args, **kwargs):\n        project = self.get_object()\n        weight = 1\n        if project.modules:\n            weight = max(\n                project.modules.values_list('weight', flat=True)\n            ) + 1\n        module = module_models.Module(\n            name=self.blueprint.title,\n            weight=weight,\n            project=project,\n            is_draft=True,\n        )\n        module.save()\n        signals.module_created.send(sender=None,\n                                    module=module,\n                                    user=self.request.user)\n\n        self._create_module_settings(module)\n        self._create_phases(module, self.blueprint.content)\n        messages.success(self.request, self.success_message)\n\n        cookie = request.COOKIES.get('dashboard_projects_closed_accordeons',\n                                     '[]')\n        ids = json.loads(parse.unquote(cookie))\n        if self.project.id not in ids:\n            ids.append(self.project.id)\n\n        cookie = parse.quote(json.dumps(ids))\n\n        response = HttpResponseRedirect(self.get_next(module))\n        response.set_cookie('dashboard_projects_closed_accordeons', cookie)\n        return response\n\n    def _create_module_settings(self, module):\n        if self.blueprint.settings_model:\n            settings_model = apps.get_model(*self.blueprint.settings_model)\n            module_settings = settings_model(module=module)\n            module_settings.save()\n\n    def _create_phases(self, module, blueprint_phases):\n        for index, phase_content in enumerate(blueprint_phases):\n            phase = phase_models.Phase(\n                type=phase_content.identifier,\n                name=phase_content.name,\n                description=phase_content.description,\n                weight=index,\n                module=module,\n            )\n            phase.save()\n\n    def get_next(self, module):\n        return reverse('a4dashboard:dashboard-module_basic-edit', kwargs={\n            'module_slug': module.slug\n        })\n\n    def get_permission_object(self):\n        return self.project\n\n\nclass ModulePublishView(SingleObjectMixin,\n                        generic.View):\n    permission_required = 'a4projects.change_project'\n    model = module_models.Module\n    slug_url_kwarg = 'module_slug'\n\n    def get_permission_object(self):\n        return self.get_object().project\n\n    def post(self, request, *args, **kwargs):\n        action = request.POST.get('action', None)\n        if action == 'publish':\n            self.publish_module()\n        elif action == 'unpublish':\n            self.unpublish_module()\n        else:\n            messages.warning(self.request, _('Invalid action'))\n\n        return HttpResponseRedirect(self.get_next())\n\n    def get_next(self):\n        if 'referrer' in self.request.POST:\n            return self.request.POST['referrer']\n        elif 'HTTP_REFERER' in self.request.META:\n            return self.request.META['HTTP_REFERER']\n\n        return reverse('a4dashboard:project-edit', kwargs={\n            'project_slug': self.project.slug\n        })\n\n    def publish_module(self):\n        module = self.get_object()\n        if not module.is_draft:\n            messages.info(self.request, _('Module is already added'))\n            return\n\n        module.is_draft = False\n        module.save()\n\n        signals.module_published.send(sender=None,\n                                      module=module,\n                                      user=self.request.user)\n\n        messages.success(self.request,\n                         _('The module is displayed in the project.'))\n\n    def unpublish_module(self):\n        module = self.get_object()\n        if module.is_draft:\n            messages.info(self.request, _('Module is already removed'))\n            return\n\n        module.is_draft = True\n        module.save()\n\n        signals.module_unpublished.send(sender=None,\n                                        module=module,\n                                        user=self.request.user)\n\n        messages.success(self.request,\n                         _('The module is no longer displayed in the project.'\n                           ))\n\n\nclass ModuleDeleteView(generic.DeleteView):\n    permission_required = 'a4projects.change_project'\n    model = module_models.Module\n    success_message = _('The module has been deleted')\n\n    def delete(self, request, *args, **kwargs):\n        messages.success(self.request, self.success_message)\n        return super().delete(request, *args, **kwargs)\n\n    def get_permission_object(self):\n        return self.get_object().project\n\n    def get_success_url(self):\n        referrer = self.request.POST.get('referrer', None) \\\n            or self.request.META.get('HTTP_REFERER', None)\n        if referrer:\n            view, args, kwargs = resolve(referrer)\n            if 'module_slug' not in kwargs \\\n                    or not kwargs['module_slug'] == self.get_object().slug:\n                return referrer\n\n        return reverse('a4dashboard:project-edit', kwargs={\n            'project_slug': self.get_object().project.slug\n        })\n\n\nclass DashboardProjectListView(a4dashboard_views.ProjectListView):\n    def get_queryset(self):\n        return super().get_queryset().filter(\n            projectcontainer=None,\n            externalproject=None\n        )\n\n\nclass ProjectCreateView(mixins.DashboardBaseMixin,\n                        SuccessMessageMixin,\n                        generic.CreateView):\n    model = project_models.Project\n    slug_url_kwarg = 'project_slug'\n    form_class = DashboardProjectCreateForm\n    template_name = 'a4dashboard/project_create_form.html'\n    permission_required = 'a4projects.add_project'\n    menu_item = 'project'\n    success_message = _('Project was created.')\n\n    def get_permission_object(self):\n        return self.organisation\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs['organisation'] = self.organisation\n        kwargs['creator'] = self.request.user\n        return kwargs\n\n    def get_success_url(self):\n        return reverse('a4dashboard:project-edit',\n                       kwargs={'project_slug': self.object.slug})\n\n    def form_valid(self, form):\n        response = super().form_valid(form)\n        signals.project_created.send(sender=None,\n                                     project=self.object,\n                                     user=self.request.user)\n\n        return response\n", "path": "meinberlin/apps/dashboard/views.py"}], "after_files": [{"content": "import json\nfrom urllib.parse import unquote\n\nfrom django import template\n\nregister = template.Library()\n\n\n@register.simple_tag(takes_context=True)\ndef closed_accordeons(context, project_id):\n    request = context['request']\n    cookie = request.COOKIES.get('dashboard_projects_closed_accordeons', '[]')\n    ids = json.loads(unquote(cookie))\n    if project_id in ids:\n        ids.append(-1)\n    return ids\n\n\n@register.filter\ndef is_publishable(project, project_progress):\n    \"\"\"Check if project can be published.\n\n    Required project details need to be filled in and at least one module\n    has to be published (added to the project).\n    \"\"\"\n    return (project_progress['project_is_complete']\n            and project.published_modules.count() >= 1)\n\n\n@register.filter\ndef has_unpublishable_modules(project):\n    \"\"\"Check if modules can be removed from project.\n\n    Modules can be removed if the project is not yet published and there is\n    another module published for (added to) the project.\n    \"\"\"\n    return (project.is_draft\n            and project.published_modules.count() > 1)\n\n\n@register.filter\ndef project_nav_is_active(dashboard_menu_project):\n    \"\"\"Check if the view is in the project dashboard nav.\"\"\"\n    for item in dashboard_menu_project:\n        if item['is_active']:\n            return True\n    return False\n\n\n@register.filter\ndef module_nav_is_active(dashboard_menu_modules):\n    \"\"\"Check if the view is in the project dashboard nav.\"\"\"\n    for module_menu in dashboard_menu_modules:\n        for item in module_menu['menu']:\n            if item['is_active']:\n                return True\n    return False\n\n\n@register.filter\ndef has_publishable_module(dashboard_menu_modules):\n    for module_menu in dashboard_menu_modules:\n        if module_menu['is_complete']:\n            return True\n    return False\n", "path": "meinberlin/apps/dashboard/templatetags/meinberlin_dashboard_tags.py"}, {"content": "import json\nfrom urllib import parse\n\nfrom django.apps import apps\nfrom django.contrib import messages\nfrom django.contrib.messages.views import SuccessMessageMixin\nfrom django.http import HttpResponseRedirect\nfrom django.urls import resolve\nfrom django.urls import reverse\nfrom django.utils.translation import ugettext_lazy as _\nfrom django.views import generic\nfrom django.views.generic.detail import SingleObjectMixin\n\nfrom adhocracy4.dashboard import mixins\nfrom adhocracy4.dashboard import signals\nfrom adhocracy4.dashboard import views as a4dashboard_views\nfrom adhocracy4.dashboard.blueprints import get_blueprints\nfrom adhocracy4.modules import models as module_models\nfrom adhocracy4.phases import models as phase_models\nfrom adhocracy4.projects import models as project_models\nfrom adhocracy4.projects.mixins import ProjectMixin\nfrom meinberlin.apps.dashboard.forms import DashboardProjectCreateForm\n\n\nclass ModuleBlueprintListView(ProjectMixin,\n                              mixins.DashboardBaseMixin,\n                              mixins.BlueprintMixin,\n                              generic.DetailView):\n    template_name = 'meinberlin_dashboard/module_blueprint_list_dashboard.html'\n    permission_required = 'a4projects.change_project'\n    model = project_models.Project\n    slug_url_kwarg = 'project_slug'\n    menu_item = 'project'\n\n    @property\n    def blueprints(self):\n        return get_blueprints()\n\n    def get_permission_object(self):\n        return self.project\n\n\nclass ModuleCreateView(ProjectMixin,\n                       mixins.DashboardBaseMixin,\n                       mixins.BlueprintMixin,\n                       SingleObjectMixin,\n                       generic.View):\n    permission_required = 'a4projects.change_project'\n    model = project_models.Project\n    slug_url_kwarg = 'project_slug'\n    success_message = _('The module was created')\n\n    def post(self, request, *args, **kwargs):\n        project = self.get_object()\n        weight = 1\n        if project.modules:\n            weight = max(\n                project.modules.values_list('weight', flat=True)\n            ) + 1\n        module = module_models.Module(\n            name=self.blueprint.title,\n            weight=weight,\n            project=project,\n            is_draft=True,\n        )\n        module.save()\n        signals.module_created.send(sender=None,\n                                    module=module,\n                                    user=self.request.user)\n\n        self._create_module_settings(module)\n        self._create_phases(module, self.blueprint.content)\n        messages.success(self.request, self.success_message)\n\n        cookie = request.COOKIES.get('dashboard_projects_closed_accordeons',\n                                     '[]')\n        ids = json.loads(parse.unquote(cookie))\n        if self.project.id not in ids:\n            ids.append(self.project.id)\n\n        cookie = parse.quote(json.dumps(ids))\n\n        response = HttpResponseRedirect(self.get_next(module))\n        response.set_cookie('dashboard_projects_closed_accordeons', cookie)\n        return response\n\n    def _create_module_settings(self, module):\n        if self.blueprint.settings_model:\n            settings_model = apps.get_model(*self.blueprint.settings_model)\n            module_settings = settings_model(module=module)\n            module_settings.save()\n\n    def _create_phases(self, module, blueprint_phases):\n        for index, phase_content in enumerate(blueprint_phases):\n            phase = phase_models.Phase(\n                type=phase_content.identifier,\n                name=phase_content.name,\n                description=phase_content.description,\n                weight=index,\n                module=module,\n            )\n            phase.save()\n\n    def get_next(self, module):\n        return reverse('a4dashboard:dashboard-module_basic-edit', kwargs={\n            'module_slug': module.slug\n        })\n\n    def get_permission_object(self):\n        return self.project\n\n\nclass ModulePublishView(SingleObjectMixin,\n                        generic.View):\n    permission_required = 'a4projects.change_project'\n    model = module_models.Module\n    slug_url_kwarg = 'module_slug'\n\n    def get_permission_object(self):\n        return self.get_object().project\n\n    def post(self, request, *args, **kwargs):\n        action = request.POST.get('action', None)\n        if action == 'publish':\n            self.publish_module()\n        elif action == 'unpublish':\n            self.unpublish_module()\n        else:\n            messages.warning(self.request, _('Invalid action'))\n\n        return HttpResponseRedirect(self.get_next())\n\n    def get_next(self):\n        if 'referrer' in self.request.POST:\n            return self.request.POST['referrer']\n        elif 'HTTP_REFERER' in self.request.META:\n            return self.request.META['HTTP_REFERER']\n\n        return reverse('a4dashboard:project-edit', kwargs={\n            'project_slug': self.project.slug\n        })\n\n    def publish_module(self):\n        module = self.get_object()\n        if not module.is_draft:\n            messages.info(self.request, _('Module is already added'))\n            return\n\n        module.is_draft = False\n        module.save()\n\n        signals.module_published.send(sender=None,\n                                      module=module,\n                                      user=self.request.user)\n\n        messages.success(self.request,\n                         _('The module is displayed in the project.'))\n\n    def unpublish_module(self):\n        module = self.get_object()\n        if module.is_draft:\n            messages.info(self.request, _('Module is already removed'))\n            return\n        if not module.project.is_draft:\n            messages.error(self.request,\n                           _('Module cannot be removed '\n                             'from a published project.'))\n            return\n        if module.project.published_modules.count() == 1:\n            messages.error(self.request,\n                           _('Module cannot be removed. '\n                             'It is the only module added to the project.'))\n            return\n\n        module.is_draft = True\n        module.save()\n\n        signals.module_unpublished.send(sender=None,\n                                        module=module,\n                                        user=self.request.user)\n\n        messages.success(self.request,\n                         _('The module is no longer displayed in the project.'\n                           ))\n\n\nclass ModuleDeleteView(generic.DeleteView):\n    permission_required = 'a4projects.change_project'\n    model = module_models.Module\n    success_message = _('The module has been deleted')\n\n    def delete(self, request, *args, **kwargs):\n        messages.success(self.request, self.success_message)\n        return super().delete(request, *args, **kwargs)\n\n    def get_permission_object(self):\n        return self.get_object().project\n\n    def get_success_url(self):\n        referrer = self.request.POST.get('referrer', None) \\\n            or self.request.META.get('HTTP_REFERER', None)\n        if referrer:\n            view, args, kwargs = resolve(referrer)\n            if 'module_slug' not in kwargs \\\n                    or not kwargs['module_slug'] == self.get_object().slug:\n                return referrer\n\n        return reverse('a4dashboard:project-edit', kwargs={\n            'project_slug': self.get_object().project.slug\n        })\n\n\nclass DashboardProjectListView(a4dashboard_views.ProjectListView):\n    def get_queryset(self):\n        return super().get_queryset().filter(\n            projectcontainer=None,\n            externalproject=None\n        )\n\n\nclass ProjectCreateView(mixins.DashboardBaseMixin,\n                        SuccessMessageMixin,\n                        generic.CreateView):\n    model = project_models.Project\n    slug_url_kwarg = 'project_slug'\n    form_class = DashboardProjectCreateForm\n    template_name = 'a4dashboard/project_create_form.html'\n    permission_required = 'a4projects.add_project'\n    menu_item = 'project'\n    success_message = _('Project was created.')\n\n    def get_permission_object(self):\n        return self.organisation\n\n    def get_form_kwargs(self):\n        kwargs = super().get_form_kwargs()\n        kwargs['organisation'] = self.organisation\n        kwargs['creator'] = self.request.user\n        return kwargs\n\n    def get_success_url(self):\n        return reverse('a4dashboard:project-edit',\n                       kwargs={'project_slug': self.object.slug})\n\n    def form_valid(self, form):\n        response = super().form_valid(form)\n        signals.project_created.send(sender=None,\n                                     project=self.object,\n                                     user=self.request.user)\n\n        return response\n", "path": "meinberlin/apps/dashboard/views.py"}]}