--- /dev/null
+<style>
+ #path {
+ font-size: 16px;
+ }
+
+ #elements {
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 90px;
+ bottom: 51px;
+ overflow-y: auto;
+ }
+
+ #elements .item .text {
+ margin: 10px 0;
+ }
+
+ #elements .item .status {
+ float: right;
+ }
+
+ #btn-open {
+ float: left;
+ }
+
+ #btn-close {
+ margin-right: 15px;
+ }
+</style>
--- /dev/null
+<header class="container-fluid">
+ <h3>{% block title %}Wgrywanie plików{% endblock %}</h3>
+ <p id="path"><span class="text-muted">Lokalizacja:</span> {{ host }}{{ 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" class="container-fluid">
+</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>
+</footer>
--- /dev/null
+{% load staticfiles %}
+
+<script src="{% static 'filex/fileupload/jquery.ui.widget.js' %}"></script>
+<script src="{% static 'filex/fileupload/jquery.iframe-transport.js' %}"></script>
+<script src="{% static 'filex/fileupload/jquery.fileupload.js' %}"></script>
+<script src="{% static 'filex/underscore/underscore-min.js' %}"></script>
+<script src="{% static 'filex/humanize/humanize-duration.js' %}"></script>
+
+<script>
+ $(function () {
+ 'use strict';
+
+ var template = _.template($('#template').html());
+
+ function humanBytes(bytes, no_unit) {
+ function format(val, unit) {
+ if(no_unit)
+ return val;
+
+ return val + ' ' + unit;
+ }
+
+ if(bytes == 0)
+ return format('0', 'B');
+
+ var k = 1024,
+ sizes = ['B', 'KB', 'MB', 'GB', 'TB'],
+ i = Math.floor(Math.log(bytes) / Math.log(k));
+
+ return format((bytes / Math.pow(k, i)).toFixed(1), sizes[i]);
+ }
+
+ $('#files').fileupload({
+ sequentialUploads: true,
+ add: function (e, data) {
+ $.each(data.files, function (index, file) {
+ data.context = $(template(file));
+ $('#elements').append(data.context);
+ });
+
+ data.submit();
+ },
+ progress: function (e, data) {
+ var progress = parseInt(data.loaded / data.total * 100, 10);
+
+ data.context.find('.progress-bar').css('width', progress+'%').attr('aria-valuenow', data.loaded);
+ data.context.find('.progress-bar span').text(progress+'%');
+
+ if (data.loaded < data.total) {
+ data.context.find('.bit-rate').text(humanBytes(data.bitrate / 8) + '/s');
+ data.context.find('.progress-info').text(humanBytes(data.loaded, true) + ' / ' + humanBytes(data.total));
+ }
+ },
+ progressall: function (e, data) {
+ var remaining = (data.total - data.loaded) / (data.bitrate / 8);
+
+ if (remaining) {
+ $('#btn-close').hide();
+ $('#status').text('Pozostało ' + (humanizeDuration(remaining * 1000, {language: 'pl', round: true}) || 'kilka sekund'));
+ }
+ else {
+ $('#btn-close').show();
+ $('#status').text('');
+ }
+ },
+ done: function (e, data) {
+ data.context.find('.bit-rate').text('');
+ data.context.find('.progress-info').text('Zakończono');
+ },
+ fail: function (e, data) {
+ data.context.find('.progress-info').text('Błąd');
+ console.log(data.errorThrown);
+ console.log(data.textStatus);
+ }
+ });
+
+ $(window).on('beforeunload', function() {
+ if ($('#files').fileupload('active'))
+ return 'Nie zakończono przesyłania wszystkich plików, czy chcesz kontynuować?';
+ });
+ });
+</script>
+
+<script type="text/template" id="template">
+ <div class="item">
+ <div class="text clearfix">
+ <span class="name"><%= name %></span>
+ <span class="status pull-right">
+ <em class="small bit-rate" style="margin-right: 15px"></em>
+ <span class="text-muted progress-info">Oczekiwanie</span>
+ </span>
+ </div>
+
+ <div class="progress">
+ <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="<%= size %>" style="width: 0;">
+ <span class="sr-only">0%</span>
+ </div>
+ </div>
+ </div>
+</script>
+from functools import wraps
+
from django.core.files.uploadedfile import UploadedFile
from django.core.files.uploadhandler import FileUploadHandler, StopUpload, StopFutureHandlers
+from django.views.decorators.csrf import csrf_exempt, csrf_protect
from filex.ftp import FTPOperation
return UploadedFile(name=self.file_name, size=file_size, charset=self.charset,
content_type=self.content_type, content_type_extra=self.content_type_extra)
+
+
+def with_ftp_upload_handler(view_func):
+ @wraps(view_func)
+ def wrapped_view(request, *args, **kwargs):
+ request.upload_handlers = [FtpUploadHandler(request)]
+
+ return csrf_protect(view_func)(request, *args, **kwargs)
+
+ return csrf_exempt(wrapped_view)
urlpatterns = patterns('',
url(r'^list/$', views.list_content, name='list'),
url(r'^download/$', views.download, name='download'),
+ url(r'^upload/$', views.upload, name='upload'),
)
from datetime import datetime
+import mimetypes
from django.core.exceptions import PermissionDenied, SuspiciousOperation
from django.http import JsonResponse, StreamingHttpResponse
from django.template.defaultfilters import filesizeformat
from django.utils.formats import date_format
from django.utils.timezone import UTC, localtime
-import mimetypes
from filex.ftp import FTPOperation, FTPException
+from filex.uploadhandler import with_ftp_upload_handler
def check_auth(request):
response['Content-Encoding'] = encoding
return response
+
+
+@with_ftp_upload_handler
+def upload(request):
+ # TODO error handling
+ return JsonResponse({'success': True})
-{% load staticfiles webdesign %}
+{% load staticfiles %}
{% load firstof from future %}
<!DOCTYPE html>
{% block extra_css %}{% endblock %}
</head>
<body>
+ {% block body %}
<nav class="navbar navbar-default navbar-static-top">
<div class="container">
<div class="navbar-header">
</div>
</div>
</footer>
+ {% endblock body %}
<script src="{% static 'qcg/jquery/jquery.min.js' %}"></script>
<script src="{% static 'qcg/bootstrap/js/bootstrap.min.js' %}"></script>
{% block extra_js %}
{% include 'filex/source.js.html' %}
+
+ <script>
+ $(function () {
+ $('#upload').click(function(e) {
+ e.preventDefault();
+
+ var url = this.href + '?' + $.param({host: filex.host, path: filex.path.full() + '/'});
+
+ var win = window.open(url, url, 'height=500,width=800');
+ win.focus();
+ });
+ })
+ </script>
{% endblock extra_js %}
{% block container %}
<div class="well well-sm">
<div class="btn-toolbar" role="toolbar">
<div class="btn-group" role="group">
- <button class="btn btn-default">Wgraj plik</button>
+ <a id="upload" href="{% url 'gridftp_upload' %}" class="btn btn-default" target="_blank">Wgraj plik</a>
<button class="btn btn-default">Utwórz katalog</button>
</div>
<div class="btn-group" role="group">
--- /dev/null
+{% extends 'qcg/base.html' %}
+
+{% block extra_css %}
+ {% include 'filex/upload.css.html' %}
+{% endblock extra_css %}
+
+{% block extra_js %}
+ {% include 'filex/upload.js.html' %}
+{% endblock extra_js %}
+
+{% block body %}
+ {% include 'filex/upload.html' %}
+{% endblock body %}
url(r'^job/(?P<job_id>[\w]+)/(?P<task_id>[\w]+)/?$', views.task_details, name='task'),
url(r'^gridftp/$', views.gridftp, name='gridftp'),
+
+ url(r'^gridftp/upload/$', views.gridftp_upload, name='gridftp_upload'),
)
@login_required
def gridftp(request):
return render(request, 'qcg/gridftp.html')
+
+
+def gridftp_upload(request):
+ # TODO GET data validation
+ return render(request, 'qcg/gridftp_upload.html',
+ {'url': reverse('filex:upload') + '?' + request.GET.urlencode(safe='/'),
+ 'host': request.GET.get('host'), 'path': request.GET.get('path')})