for attr in attrs.split(';'):
try:
key, value = attr.split('=', 1)
- attrs_dict[key] = value
except ValueError:
- pass
+ key, value = attr, ''
+
+ attrs_dict[key] = value
yield {
'name': name,
- 'type': 'directory' if attrs_dict['Type'] == 'dir' else 'file',
+ 'type': 'directory' if attrs_dict['Type'].endswith('dir') else 'file',
'size': int(attrs_dict['Size']),
'date': localtime(datetime.strptime(attrs_dict['Modify'], "%Y%m%d%H%M%S").replace(tzinfo=UTC())),
}
$this.modal('hide');
$btn.button('reset');
- }, 'json').fail(function() {
+ }, 'json').fail(function(xhr) {
console.error(arguments);
- $btn.button('error');
+ var error = (xhr.responseJSON || {}).error || undefined;
+
+ if (typeof error === 'string') {
+ $('<div>', {
+ 'class': 'alert alert-danger',
+ html: '<span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> ' + error
+ }).prependTo($this.find('.modal-body'));
+ $btn.button('reset');
+ }
+ else {
+ $btn.button('error');
+ }
});
});
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Anuluj</button>
- <button type="submit" class="btn btn-primary" data-loading-text="Zapisywanie..." data-error-text="Błąd">OK</button>
+ <button type="submit" class="btn btn-primary" data-loading-text="Zapisywanie..." data-error-text="Błąd serwera">OK</button>
</div>
</div>
</div>
<p id="path"><span class="text-muted">Lokalizacja:</span> {{ host }}{{ sep }}{{ path }}</p>
</header>
-<form enctype="multipart/form-data" method="post" action="{{ url }}" hidden>
- {% csrf_token %}
- <input id="files" type="file" name="files" multiple>
-</form>
-
-<div id="elements"></div>
-
-<div id="drop-overlay" class="fade">Upuść pliki tutaj</div>
-
-<footer class="navbar navbar-default navbar-fixed-bottom">
+{% if error %}
<div class="container-fluid">
- <button id="btn-open" class="btn btn-default navbar-btn" onclick="$('#files').click()">Wybierz pliki</button>
- <p class="navbar-text">lub przeciągnij je w obszar tego okna</p>
- <div class="navbar-right">
- <p id="status" class="navbar-text"></p>
- <button id="btn-close" class="btn btn-default navbar-btn" onclick="window.opener.filex.reloadFiles(); window.close()">Zamknij</button>
+ <div class="alert alert-danger">
+ <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span> {{ error }}
</div>
</div>
-</footer>
+{% else %}
+ <form enctype="multipart/form-data" method="post" action="{{ url }}" hidden>
+ {% csrf_token %}
+ <input id="files" type="file" name="files" multiple>
+ </form>
-<div id="conflict" class="modal" tabindex="-1" role="dialog" aria-labelledby="modal-label" aria-hidden="true">
- <div class="modal-dialog">
- <div class="modal-content">
- <div class="modal-header">
- <h4 class="modal-title" id="modal-label">Plik już istnieje</h4>
- </div>
- <div class="modal-body">
+ <div id="elements"></div>
+
+ <div id="drop-overlay" class="fade">Upuść pliki tutaj</div>
+
+ <footer class="navbar navbar-default navbar-fixed-bottom">
+ <div class="container-fluid">
+ <button id="btn-open" class="btn btn-default navbar-btn" onclick="$('#files').click()">Wybierz pliki</button>
+ <p class="navbar-text">lub przeciągnij je w obszar tego okna</p>
+ <div class="navbar-right">
+ <p id="status" class="navbar-text"></p>
+ <button id="btn-close" class="btn btn-default navbar-btn" onclick="window.opener.filex.reloadFiles(); window.close()">Zamknij</button>
</div>
- <div class="modal-footer">
- <div class="checkbox pull-left">
- <label>
- <input id="apply-to-all" type="checkbox" autocomplete="off"> Zastosuj do wszystkich
- </label>
+ </div>
+ </footer>
+
+ <div id="conflict" class="modal" tabindex="-1" role="dialog" aria-labelledby="modal-label" aria-hidden="true">
+ <div class="modal-dialog">
+ <div class="modal-content">
+ <div class="modal-header">
+ <h4 class="modal-title" id="modal-label">Plik już istnieje</h4>
+ </div>
+ <div class="modal-body">
+ </div>
+ <div class="modal-footer">
+ <div class="checkbox pull-left">
+ <label>
+ <input id="apply-to-all" type="checkbox" autocomplete="off"> Zastosuj do wszystkich
+ </label>
+ </div>
+ <button id="btn-skip" type="button" class="btn btn-default">Pomiń</button>
+ <button id="btn-replace" type="button" class="btn btn-default">Zastąp</button>
</div>
- <button id="btn-skip" type="button" class="btn btn-default">Pomiń</button>
- <button id="btn-replace" type="button" class="btn btn-default">Zastąp</button>
</div>
</div>
</div>
-</div>
+{% endif %}
});
$(window).on('beforeunload', function() {
- if ($('#files').fileupload('active'))
+ var $files = $('#files');
+ if ($files.length && $files.fileupload('active'))
return 'Nie zakończono przesyłania wszystkich plików, czy chcesz kontynuować?';
});
from functools import wraps
from django.core.validators import RegexValidator
-from django.http import JsonResponse
+from django.http import JsonResponse, HttpResponse
+from django.template.loader import render_to_string
from django.views.decorators.csrf import csrf_protect, csrf_exempt
from filex.ftp import FTPError
-from filex.uploadhandler import FtpUploadHandler
-msg = u'Invalid value'
+_msg = u'Invalid value'
host_validator = RegexValidator(r'^(?:(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+'
- r'(?:[a-zA-Z]{2,6}\.?|[a-zA-Z0-9-]{2,}(?<!-)\.?))(?::\d+)?$', msg)
-path_validator = RegexValidator(r'^~?(?:/[^/\0]*)*$', msg)
-name_validator = RegexValidator(r'^[^/\0]+$', msg)
+ r'(?:[a-zA-Z]{2,6}\.?|[a-zA-Z0-9-]{2,}(?<!-)\.?))(?::\d+)?$', _msg)
+path_validator = RegexValidator(r'^~?(?:/[^/\0]*)*$', _msg)
+name_validator = RegexValidator(r'^[^/\0]+$', _msg)
def with_ftp_upload_handler(view_func):
+ from filex.uploadhandler import FtpUploadHandler
+
@wraps(view_func)
def wrapped_view(request, *args, **kwargs):
request.upload_handlers = [FtpUploadHandler(request)]
try:
return csrf_protect(view_func)(request, *args, **kwargs)
except FTPError as e:
- status = 400
- if 'No such file or directory' in e.message:
- status = 404
- elif 'Permission denied' in e.message:
- status = 403
+ msg, status = parse_ftp_error(e)
- return JsonResponse({'error': e.message}, status=status)
+ return JsonResponse({'error': msg}, status=status)
return csrf_exempt(wrapped_view)
+
+
+def parse_ftp_error(e):
+ msg, status = e.message, 400
+
+ if 'No such file or directory' in msg:
+ status = 404
+ elif 'Permission denied' in msg:
+ status = 403
+
+ return msg, status
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.http import JsonResponse, StreamingHttpResponse
-from django.shortcuts import get_object_or_404
+from django.shortcuts import get_object_or_404, render
from django.template.defaultfilters import filesizeformat
from django.utils.formats import date_format
from django.views.decorators.http import require_POST
CompressForm
from filex.ftp import FTPOperation, FTPError
from filex.models import Favorite
-from filex.uploadhandler import with_ftp_upload_handler
+from filex.utils import with_ftp_upload_handler, parse_ftp_error
class FTPView(View):
method = 'get'
form_class = HostPathForm
+ request = None
@classmethod
def as_view(cls, **initkwargs):
if not form.is_valid():
return JsonResponse({'error': form.errors}, status=400)
+ self.request = request
try:
return self.handle(FTPOperation(request.session['proxy']), form.cleaned_data)
except FTPError as e:
- status = 400
- if 'No such file or directory' in e.message:
- status = 404
- elif 'Permission denied' in e.message:
- status = 403
+ msg, status = parse_ftp_error(e)
- return JsonResponse({'error': e.message}, status=status)
+ return JsonResponse({'error': msg}, status=status)
setattr(cls, cls.method, process)
class DownloadView(FTPView):
def handle(self, ftp, params):
- data = ftp.get(make_url(params, 'path'))
+ url = make_url(params, 'path')
+
+ try:
+ stats = ftp.info(url)
+ except FTPError as e:
+ msg, status = parse_ftp_error(e)
+
+ return render(self.request, 'qcg/download_error.html', {'msg': msg, 'url': url}, status=status)
+
+ data = ftp.get(url)
name = os.path.basename(params['path'])
mime_type, encoding = mimetypes.guess_type(name)
response = StreamingHttpResponse(data, content_type=mime_type or 'application/octet-stream')
response['Content-Disposition'] = u'attachment; filename={}'.format(name)
- # TODO Content-Length (?)
+ response['Content-Length'] = stats['size']
if encoding:
response['Content-Encoding'] = encoding
form = FavoriteForm(data)
- # TODO check if path exists
- if form.is_valid():
- instance = form.save()
+ if not form.is_valid():
+ return JsonResponse({'error': form.errors}, status=400)
+
+ try:
+ FTPOperation(request.session['proxy']).info(make_url(form.cleaned_data, 'path'))
+ except FTPError as e:
+ msg, status = parse_ftp_error(e)
+
+ return JsonResponse({'error': msg}, status=status)
- return JsonResponse({'group': 'usr', 'host': instance.host, 'path': instance.path,
- 'value': instance.host + '/' + instance.path})
+ instance = form.save()
- return JsonResponse({'error': form.errors}, status=400)
+ return JsonResponse({'group': 'usr', 'host': instance.host, 'path': instance.path,
+ 'value': instance.host + '/' + instance.path})
@require_POST
--- /dev/null
+{% extends 'qcg/base.html' %}
+
+{% block container %}
+ <h1 class="page-header">{% block title %}Błąd pobierania{% endblock %}</h1>
+
+ <h3>{{ msg }}</h3>
+ <h2><small>{{ url }}</small></h2>
+{% endblock container %}
from openid.extensions import ax
from filex.forms import HostPathNameForm, RenameForm, ArchiveForm, HostPathForm
+from filex.ftp import FTPOperation, FTPError
+from filex.views import make_url
from qcg.forms import FiltersForm, ColumnsForm, JobDescriptionForm, EnvFormSet
from qcg.utils import paginator_context
from qcg.service import update_user_data, submit_job
@login_required
def gridftp(request):
return render(request, 'qcg/gridftp.html',
- {'new_dir_form': HostPathNameForm(), 'rename_form': RenameForm(), 'archive_form': ArchiveForm()})
+ {'new_dir_form': HostPathNameForm(), 'rename_form': RenameForm(), 'archive_form': ArchiveForm()})
@login_required
if not form.is_valid():
raise SuspiciousOperation('Invalid parameters for `gridftp_upload`!')
+ error = None
+ try:
+ FTPOperation(request.session['proxy']).info(make_url(form.cleaned_data, 'path'))
+ except FTPError as e:
+ error = e.message
+
return render(request, 'qcg/gridftp_upload.html',
- {'url': reverse('filex:upload') + '?' + urlencode(form.cleaned_data),
+ {'error': error, 'url': reverse('filex:upload') + '?' + urlencode(form.cleaned_data),
'host': form.cleaned_data['host'], 'path': form.cleaned_data['path'],
'sep': '/' if form.cleaned_data['path'].startswith('~') else ''})