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