|
| 1 | +import warnings |
1 | 2 | from copy import deepcopy |
| 3 | +from pathlib import Path |
2 | 4 |
|
3 | 5 | from django.apps import apps |
4 | 6 | from django.conf import settings |
5 | 7 | from django.contrib import messages |
6 | 8 | from django.core.exceptions import PermissionDenied |
| 9 | +from django.core.files.base import ContentFile |
7 | 10 | from django.shortcuts import get_object_or_404 |
8 | 11 | from django.shortcuts import redirect |
9 | 12 | from django.urls import NoReverseMatch |
@@ -107,29 +110,61 @@ def get_context_data(self, **kwargs): |
107 | 110 |
|
108 | 111 |
|
109 | 112 | class DashboardComponentFormSignalMixin(edit.FormMixin): |
| 113 | + """Mixin which sends a project_component_updated or module_component_updated signal |
| 114 | + when creating, updating or deleting an object via the HTTP POST method |
| 115 | + (e.g. from the dashboard). |
| 116 | + """ |
| 117 | + |
110 | 118 | def form_valid(self, form): |
| 119 | + # The call to super.form_valid() may delete self.project or self.module, |
| 120 | + # to be able to still reference them below we need to store them in separate |
| 121 | + # variables before they are deleted. |
| 122 | + project = self.project |
| 123 | + module = self.module |
| 124 | + |
111 | 125 | response = super().form_valid(form) |
112 | 126 |
|
113 | 127 | component = self.component |
114 | 128 | if component.identifier in components.projects: |
115 | 129 | signals.project_component_updated.send( |
116 | 130 | sender=component.__class__, |
117 | | - project=self.project, |
| 131 | + project=project, |
118 | 132 | component=component, |
119 | 133 | user=self.request.user, |
120 | 134 | ) |
121 | 135 | else: |
122 | 136 | signals.module_component_updated.send( |
123 | 137 | sender=component.__class__, |
124 | | - module=self.module, |
| 138 | + module=module, |
125 | 139 | component=component, |
126 | 140 | user=self.request.user, |
127 | 141 | ) |
128 | 142 | return response |
129 | 143 |
|
130 | 144 |
|
131 | 145 | class DashboardComponentDeleteSignalMixin(edit.DeletionMixin): |
| 146 | + """Deprecated, use DashboardComponentFormSignalMixin for POST requests instead. |
| 147 | + This mixin will be removed in the next version. |
| 148 | + """ |
| 149 | + |
| 150 | + def __init__(self): |
| 151 | + warnings.warn( |
| 152 | + "dashboard.mixins.DashboardComponentDeleteSignalMixin is deprecated, " |
| 153 | + "use dashboard.mixins.DashboardComponentFormSignalMixin for forms / POST " |
| 154 | + "requests. This mixin will be removed in the next version.", |
| 155 | + DeprecationWarning, |
| 156 | + ) |
| 157 | + |
| 158 | + super().__init__() |
| 159 | + |
132 | 160 | def delete(self, request, *args, **kwargs): |
| 161 | + warnings.warn( |
| 162 | + "dashboard.mixins.DashboardComponentDeleteSignalMixin.delete()" |
| 163 | + "is deprecated, use dashboard.mixins.DashboardComponentFormSignalMixin " |
| 164 | + "for forms / POST requests. This mixin will be removed in the next " |
| 165 | + "version.", |
| 166 | + DeprecationWarning, |
| 167 | + ) |
133 | 168 | # Project and module have to be stored before delete is called as |
134 | 169 | # they may rely on the still existing db object. |
135 | 170 | project = self.project |
@@ -167,16 +202,31 @@ def post(self, request, *args, **kwargs): |
167 | 202 |
|
168 | 203 | project_clone = deepcopy(project) |
169 | 204 | project_clone.pk = None |
170 | | - if project_clone.tile_image: |
171 | | - project_clone.tile_image.save( |
172 | | - project.tile_image.name, project.tile_image, False |
173 | | - ) |
174 | | - if project_clone.image: |
175 | | - project_clone.image.save(project.image.name, project.image, False) |
176 | 205 | project_clone.created = timezone.now() |
177 | 206 | project_clone.is_draft = True |
178 | 207 | project_clone.is_archived = False |
| 208 | + |
| 209 | + if project.tile_image: |
| 210 | + # django's image.name contains the file storage location defined in 'upload_to' |
| 211 | + tile_file_name = Path( |
| 212 | + project.tile_image.name |
| 213 | + ).name # retreives only the filename |
| 214 | + tile_copy = ContentFile( |
| 215 | + project.tile_image.read() |
| 216 | + ) # retreives the image bytes |
| 217 | + project_clone.tile_image.save(tile_file_name, tile_copy, False) |
| 218 | + |
| 219 | + if project.image: |
| 220 | + # django's image.name contains the file storage location defined in 'upload_to' |
| 221 | + image_file_name = Path(project.image.name).name |
| 222 | + image_copy = ContentFile(project.image.read()) |
| 223 | + project_clone.image.save(image_file_name, image_copy, False) |
| 224 | + |
179 | 225 | project_clone.save() |
| 226 | + if project.topics: |
| 227 | + for topic in project.topics.all(): |
| 228 | + project_clone.topics.add(topic) |
| 229 | + |
180 | 230 | signals.project_created.send( |
181 | 231 | sender=None, project=project_clone, user=self.request.user |
182 | 232 | ) |
|
0 commit comments