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