1904d4f91a6b52679ff421f0a5ae4f55c6231ec6
[qcg-portal.git] / filex / views.py
1 from itertools import islice
2 import logging
3 import mimetypes
4 import os
5
6 from django.contrib.auth.decorators import login_required
7 from django.core.exceptions import PermissionDenied
8 from django.http import JsonResponse, StreamingHttpResponse
9 from django.shortcuts import get_object_or_404, render
10 from django.template.defaultfilters import filesizeformat
11 from django.utils.formats import date_format
12 from django.views.decorators.http import require_POST
13 from django.views.generic import View
14
15 from filex.forms import HostPathNameForm, RenameForm, FavoriteForm, HostPathForm, ExtractForm, HostItemsForm, \
16     CompressForm
17 from filex.ftp import FTPOperation, FTPError
18 from filex.models import Favorite
19 from filex.utils import with_ftp_upload_handler, parse_ftp_error
20
21
22 class FTPView(View):
23     method = 'get'
24     form_class = HostPathForm
25     request = None
26
27     @classmethod
28     def as_view(cls, **initkwargs):
29         def process(self, request):
30             if not request.user.is_authenticated():
31                 raise PermissionDenied("Login required!")
32             if not request.session['proxy']:
33                 raise PermissionDenied("No proxy found!")
34
35             form = self.form_class(request.POST if self.method == 'post' else request.GET)
36
37             if not form.is_valid():
38                 return JsonResponse({'error': form.errors}, status=400)
39
40             self.request = request
41             try:
42                 return self.handle(FTPOperation(request.session['proxy']), form.cleaned_data)
43             except FTPError as e:
44                 msg, status = parse_ftp_error(e)
45
46                 logger = logging.getLogger('gridftp')
47                 logger.error(e.verbose, extra={'user': request.user, 'path': request.path, 'params': form.cleaned_data})
48
49                 return JsonResponse({'error': msg}, status=status)
50
51         setattr(cls, cls.method, process)
52
53         return super(FTPView, cls).as_view(**initkwargs)
54
55     def handle(self, ftp, params):
56         raise NotImplementedError
57
58
59 class ListView(FTPView):
60     def handle(self, ftp, params):
61         listing = ftp.listing(make_url(params, 'path'))
62
63         data = []
64         # ignore . and .. from beginning of the listing
65         for item in islice(listing, 2, None):
66             item['size'] = filesizeformat(item['size'])
67             item['date'] = date_format(item['date'], 'CUSTOM_DATETIME_FORMAT')
68
69             data.append(item)
70
71         return JsonResponse(data, safe=False)
72
73
74 class DownloadView(FTPView):
75     def handle(self, ftp, params):
76         url = make_url(params, 'path')
77
78         try:
79             stats = ftp.info(url)
80         except FTPError as e:
81             msg, status = parse_ftp_error(e)
82
83             return render(self.request, 'qcg/download_error.html', {'msg': msg, 'url': url}, status=status)
84
85         data = ftp.get(url)
86
87         name = os.path.basename(params['path'])
88         mime_type, encoding = mimetypes.guess_type(name)
89
90         response = StreamingHttpResponse(data, content_type=mime_type or 'application/octet-stream')
91         response['Content-Disposition'] = u'attachment; filename={}'.format(name)
92         response['Content-Length'] = stats['size']
93
94         if encoding:
95             response['Content-Encoding'] = encoding
96
97         return response
98
99
100 class InfoView(FTPView):
101     def handle(self, ftp, params):
102         return JsonResponse(ftp.info(make_url(params, 'path')))
103
104
105 class DeleteView(FTPView):
106     method = 'post'
107     form_class = HostItemsForm
108
109     def handle(self, ftp, params):
110         url = make_url(params)
111         done, fail = [], {}
112
113         for path in params['dirs']:
114             try:
115                 ftp.rmdir(url + path)
116             except FTPError as e:
117                 fail[path] = e.message
118             else:
119                 done.append(path)
120
121         for path in params['files']:
122             try:
123                 ftp.delete(url + path)
124             except FTPError as e:
125                 fail[path] = e.message
126             else:
127                 done.append(path)
128
129         return JsonResponse({'done': done, 'fail': fail})
130
131
132 class MkdirView(FTPView):
133     method = 'post'
134     form_class = HostPathNameForm
135
136     def handle(self, ftp, params):
137         ftp.mkdir(make_url(params, 'path', 'name'))
138
139         return JsonResponse({'success': True})
140
141
142 class MoveView(FTPView):
143     method = 'post'
144     form_class = RenameForm
145
146     def handle(self, ftp, params):
147         ftp.move(make_url(params, 'src'), make_url(params, 'dst'))
148
149         return JsonResponse({'success': True})
150
151
152 class CompressView(FTPView):
153     method = 'post'
154     form_class = CompressForm
155
156     def handle(self, ftp, params):
157         try:
158             # consume generator with command output
159             list(ftp.compress(make_url(params), params['path'], params['files'], params['archive']))
160         except ValueError as e:
161             return JsonResponse({'error': e.message}, status=400)
162
163         return JsonResponse({'success': True})
164
165
166 class ExtractView(FTPView):
167     method = 'post'
168     form_class = ExtractForm
169
170     def handle(self, ftp, params):
171         try:
172             # consume generator with command output
173             list(ftp.extract(make_url(params), params['path'], params['dst']))
174         except ValueError as e:
175             return JsonResponse({'error': e.message}, status=400)
176
177         return JsonResponse({'success': True})
178
179
180 def make_url(params, *parts):
181     return 'gsiftp://{}/{}'.format(params['host'], os.path.join(*[params[part] for part in parts]) if parts else '')
182
183
184 @require_POST
185 @with_ftp_upload_handler
186 def upload(request):
187     return JsonResponse({'success': True})
188
189
190 @require_POST
191 @login_required
192 def fav_add(request):
193     data = request.POST.copy()
194     data['owner'] = request.user.id
195
196     form = FavoriteForm(data)
197
198     if not form.is_valid():
199         return JsonResponse({'error': form.errors}, status=400)
200
201     try:
202         FTPOperation(request.session['proxy']).info(make_url(form.cleaned_data, 'path'))
203     except FTPError as e:
204         msg, status = parse_ftp_error(e)
205
206         return JsonResponse({'error': msg}, status=status)
207
208     instance = form.save()
209
210     return JsonResponse({'group': 'usr', 'host': instance.host, 'path': instance.path,
211                          'value': instance.host + '/' + instance.path})
212
213
214 @require_POST
215 @login_required
216 def fav_delete(request):
217     fav = get_object_or_404(Favorite, owner=request.user, host=request.POST['host'], path=request.POST['path'])
218     fav.delete()
219
220     return JsonResponse({'success': True})