Skip to content

Commit bde768a

Browse files
committed
add endpoint to crop image
1 parent c7f0f88 commit bde768a

File tree

2 files changed

+36
-7
lines changed

2 files changed

+36
-7
lines changed

finder/browser/urls.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
'<uuid:file_id>/change',
3939
BrowserView.as_view(action='change'),
4040
),
41+
path(
42+
'<uuid:image_id>/crop',
43+
BrowserView.as_view(action='crop'),
44+
),
4145
path(
4246
'jsi18n/',
4347
JavaScriptCatalog.as_view(packages=['finder']),

finder/browser/views.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
from django.contrib.sites.shortcuts import get_current_site
22
from django.core.exceptions import BadRequest, ObjectDoesNotExist
3+
from django.core.files.storage import default_storage
34
from django.db.models import QuerySet, Subquery
45
from django.forms.renderers import DjangoTemplates
5-
from django.http import JsonResponse, HttpResponseBadRequest
6+
from django.http import JsonResponse, HttpResponseBadRequest, HttpResponseNotFound
7+
from django.utils.decorators import method_decorator
68
from django.utils.html import strip_spaces_between_tags
79
from django.utils.safestring import mark_safe
810
from django.views import View
11+
from django.views.decorators.http import require_GET, require_http_methods, require_POST
912

1013
from finder.lookups import annotate_unified_queryset, lookup_by_label, sort_by_attribute
1114
from finder.models.file import FileModel
@@ -30,6 +33,8 @@ class BrowserView(View):
3033
limit = 25
3134

3235
def dispatch(self, request, *args, **kwargs):
36+
if self.action is None:
37+
return HttpResponseNotFound()
3338
action = getattr(self, self.action, None)
3439
if not callable(action):
3540
return HttpResponseBadRequest(f"Action {self.action} not allowed.")
@@ -62,6 +67,7 @@ def _get_realm(self, request, slug):
6267
except RealmModel.DoesNotExist:
6368
raise ObjectDoesNotExist(f"Realm named {slug} not found for {site.domain}.")
6469

70+
@method_decorator(require_GET)
6571
def structure(self, request, slug):
6672
realm = self._get_realm(request, slug)
6773
root_folder_id = str(realm.root_folder.id)
@@ -100,6 +106,7 @@ def structure(self, request, slug):
100106
**self.list(request, request.session['finder.last_folder']),
101107
}
102108

109+
@method_decorator(require_GET)
103110
def fetch(self, request, inode_id):
104111
"""
105112
Open the given folder and fetch children data for the folder.
@@ -121,6 +128,7 @@ def fetch(self, request, inode_id):
121128
else:
122129
return inode.as_dict
123130

131+
@method_decorator(require_GET)
124132
def open(self, request, folder_id):
125133
"""
126134
Just open the folder.
@@ -132,6 +140,7 @@ def open(self, request, folder_id):
132140
request.session.modified = True
133141
return {'id': folder_id}
134142

143+
@method_decorator(require_GET)
135144
def close(self, request, folder_id):
136145
"""
137146
Just close the folder.
@@ -145,6 +154,7 @@ def close(self, request, folder_id):
145154
request.session.modified = True
146155
return {'id': folder_id}
147156

157+
@method_decorator(require_GET)
148158
def list(self, request, folder_id):
149159
"""
150160
List all the files of the given folder.
@@ -175,6 +185,7 @@ def list(self, request, folder_id):
175185
'search_query': '',
176186
}
177187

188+
@method_decorator(require_GET)
178189
def search(self, request, folder_id):
179190
"""
180191
Search for files in either the descendants of given folder or in all folders.
@@ -207,12 +218,11 @@ def search(self, request, folder_id):
207218
'offset': next_offset,
208219
}
209220

221+
@method_decorator(require_POST)
210222
def upload(self, request, folder_id):
211223
"""
212224
Upload a single file into the given folder.
213225
"""
214-
if request.method != 'POST':
215-
raise BadRequest(f"Method {request.method} not allowed. Only POST requests are allowed.")
216226
if request.content_type != 'multipart/form-data' or 'upload_file' not in request.FILES:
217227
raise BadRequest("Bad form encoding or missing payload.")
218228
model = FileModel.objects.get_model_for(request.FILES['upload_file'].content_type)
@@ -230,22 +240,37 @@ def upload(self, request, folder_id):
230240
}
231241
return response
232242

243+
@method_decorator(require_http_methods(['DELETE', 'POST']))
233244
def change(self, request, file_id):
234245
"""
235246
Change some fields after uploading a single file.
236247
"""
237-
if request.method not in ['DELETE', 'POST']:
238-
raise BadRequest(f"Method {request.method} not allowed. Only POST and DELETE requests are allowed.")
239-
if request.method == 'POST' and request.content_type != 'multipart/form-data':
240-
raise BadRequest("Bad form encoding or missing payload.")
241248
file = FileModel.objects.get_inode(id=file_id)
242249
if request.method == 'DELETE':
243250
file.delete()
244251
return {'file_info': None}
252+
if request.content_type != 'multipart/form-data':
253+
raise BadRequest("Bad form encoding or missing payload.")
245254
form_class = file.get_form_class()
246255
form = form_class(instance=file, data=request.POST, renderer=FormRenderer())
247256
if form.is_valid():
248257
file = form.save()
249258
return {'file_info': file.as_dict}
250259
else:
251260
return {'form_html': mark_safe(strip_spaces_between_tags(form.as_div()))}
261+
262+
@method_decorator(require_POST)
263+
def crop(self, request, image_id):
264+
image = FileModel.objects.get_inode(id=image_id, mime_types=['image/*'], is_folder=False)
265+
width, height = int(request.POST.get('width')), int(request.POST.get('height'))
266+
cropped_image_path = image.get_cropped_path(width, height)
267+
if not default_storage.exists(cropped_image_path):
268+
image.crop(cropped_image_path, width, height)
269+
cropped_image_url = default_storage.url(cropped_image_path)
270+
return {
271+
'image_id': image_id,
272+
'alt_text': image.meta_data.get('alt_text', image.name),
273+
'cropped_image_url': cropped_image_url,
274+
'width': width,
275+
'height': height,
276+
}

0 commit comments

Comments
 (0)