diff --git a/README.md b/README.md index 8f185a9..a5596da 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,8 @@ -[jQuery-File-Upload](http://aquantum-demo.appspot.com/file-upload) is developed by Sebastian Tschan, with the source available on [github](https://github.com/blueimp/jQuery-File-Upload). Example code is [ported to Django](https://github.com/sigurdga/django-jquery-file-upload) by Sigurd Gartmann ([sigurdga on github](https://github.com/sigurdga/)). +Disclaimer +========== -Introduction -============ - -This is a small example on how to setup Sebastian Tschan's jQuery File Upload in Django. He has a working demo on his [webpage](http://aquantum-demo.appspot.com/file-upload) and a [github repository](https://github.com/blueimp/jQuery-File-Upload) with an example on how to do it in PHP. - -Here, you'll find a minimal Django project with a minimal app. You can run the example standalone by cloning the repository, running the migrations and starting the server. - -I want to give a thank to [Sebastian Tschan](https://github.com/blueimp), the original author, [Etay Cohen-Solal](https://github.com/et-cs), for the latest major update, and [Jørgen Bergquist](https://github.com/bergquis) for helping me over the first hurdles. - -Features -======== +This project is a fork -* Drag and drop files -* Select multiple files -* Cancel upload -* Delete uploaded file (from database only) -* No flash (or other browser plugins) needed -* … more at the [upstream's features page](http://aquantum-demo.appspot.com/file-upload#features) Requirements ============ @@ -37,6 +22,25 @@ Installation * python manage.py runserver * go to localhost:8000/upload/new/ and upload some files +Developing with docker +====================== + $ docker run -ti --name uploaderdevel -p 8000:8000 -v /workdir/django_uploader:/devel python:2.7.18 bash + $ pip install -r requirements.txt + $ export DJANGO_SETTINGS_MODULE=settings_dev + $ python manage.py runserver 0.0.0.0:8000 + +Database migrations +=================== + +From existing DB + $ python manage.py schemamigration fileupload --initial + $ python manage.py migrate --fake + + +Generate migration and apply + $ python manage.py schemamigration fileupload --auto + $ python manage.py migrate fileupload + License ======= MIT, as the original project. See LICENSE.txt. diff --git a/core/wsgi.py b/core/wsgi.py new file mode 100644 index 0000000..5151301 --- /dev/null +++ b/core/wsgi.py @@ -0,0 +1,58 @@ +""" +WSGI config for finto project. + +This module contains the WSGI application used by Django's development server +and any production WSGI deployments. It should expose a module-level variable +named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover +this application via the ``WSGI_APPLICATION`` setting. + +Usually you will have the standard Django WSGI application here, but it also +might make sense to replace the whole Django WSGI application with a custom one +that later delegates to the Django one. For example, you could introduce WSGI +middleware here, or combine a Django application with an application of another +framework. + +""" +import os +import sys +import site + +ALLDIRS = ['/var/www/djuploader/lib/python2.7/site-packages'] + +path = '/var/www/djuploader/dj-src' +if path not in sys.path: + sys.path.append(path) + +# Remember original sys.path. +prev_sys_path = list(sys.path) + +# Add each new site-packages directory. +for directory in ALLDIRS: + site.addsitedir(directory) + +# Reorder sys.path so new directories at the front. +new_sys_path = [] +for item in list(sys.path): + if item not in prev_sys_path: + new_sys_path.append(item) + sys.path.remove(item) +sys.path[:0] = new_sys_path + +activate_this = '/var/www/djuploader/bin/activate_this.py' +execfile(activate_this, dict(__file__=activate_this)) + +# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks +# if running multiple sites in the same mod_wsgi process. To fix this, use +# mod_wsgi daemon mode with each site in its own daemon process, or use +# os.environ["DJANGO_SETTINGS_MODULE"] = "finto.settings" +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") + +# This application object is used by any WSGI server configured to use this +# file. This includes Django's development server, if the WSGI_APPLICATION +# setting points here. +from django.core.wsgi import get_wsgi_application +application = get_wsgi_application() + +# Apply WSGI middleware here. +# from helloworld.wsgi import HelloWorldApplication +# application = HelloWorldApplication(application) diff --git a/deploy/apache/apache.conf b/deploy/apache/apache.conf new file mode 100644 index 0000000..0612f85 --- /dev/null +++ b/deploy/apache/apache.conf @@ -0,0 +1,52 @@ +# WSGISocketPrefix +WSGISocketPrefix /var/run/wsgi +WSGIPythonPath /path_to_app:/path_to_virtualenv/lib/python2.7/site-packages + +################### +# web uploads +# + +# config from http://wiki.apache.org/httpd/NameBasedSSLVHosts + SSLEngine on + SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP + + SSLCertificateFile /etc/ssl/certs/cert.com.crt + SSLCertificateKeyFile /etc/ssl/certs/cert.com.key + SSLCertificateChainFile /etc/ssl/certs/StandardSSLCA.pem + SSLVerifyClient None + + ServerName "foo" + ServerAdmin "webmaster@foo" + + CustomLog "/var/log/httpd/upload-access.log" combined + ErrorLog "/var/log/httpd/upload-error.log" + + # Django file uploader + ################################################### + Alias /media/ /var/media-storage/ + AliasMatch ^/([^/]*\.css) /path_to_app/static/css/$1 + Alias /static/ /path_to_app/static/ + + + Require all granted + + + + Require all denied + + + WSGIScriptAlias /secureupload /path_to_app/core/wsgi.py + + + SSLRequireSSL + + + Require all granted + + + + +# END WEBUPLOADS +################# + +# /* vim: set expandtab tabstop=4 shiftwidth=4: */ diff --git a/deploy/selinux/djuploader2.0.te b/deploy/selinux/djuploader2.0.te new file mode 100644 index 0000000..9e96269 --- /dev/null +++ b/deploy/selinux/djuploader2.0.te @@ -0,0 +1,12 @@ + +module djuploader 1.0; + +require { + type httpd_t; + type var_t; + class file { getattr unlink }; +} + +#============= httpd_t ============== +allow httpd_t var_t:file unlink; +allow httpd_t var_t:file getattr; diff --git a/fileupload/admin.py b/fileupload/admin.py index faa5cdc..c924e8d 100644 --- a/fileupload/admin.py +++ b/fileupload/admin.py @@ -1,4 +1,98 @@ -from fileupload.models import Picture +from fileupload.models import File from django.contrib import admin +from django.http import HttpResponse +import tarfile +import shutil +from django.contrib import messages +from django.conf import settings +import os.path, time +import datetime as dt -admin.site.register(Picture) \ No newline at end of file +def validate_size(files, maxsize): + total_size = 0 + if len(files) > 1: + for file in files: + total_size += file.file.size + + return total_size <= maxsize + return True + +def make_download(modeladmin, request, queryset): + + try: + response = HttpResponse(mimetype='application/x-gzip') + response['Content-Disposition'] = 'attachment; filename=MD_secure_upload_download.tar.gz' + tarred = tarfile.open(fileobj=response, mode='w:gz') + + + if not validate_size(queryset,1000000000): + messages.warning(request, "Download group is too big, unselect some files or select just 1, max size of multple download: 1.0 GB") + + else: + + for file in queryset: + tarred.add(settings.MEDIA_ROOT+file.file.name, arcname=file.file.name ) + tarred.close() + + return response + + except tarfile.TarError: + messages.error(request, "Error while creating the TAR archive, contact the webmaster.") + except : + messages.error(request, "There was an error, please contact the webmaster") + + +make_download.short_description = "Download selected files" + + +def make_archive(modeladmin, request, queryset): + + try: + + for file in queryset: + archive_dir = os.path.join(settings.ARCHIVE_ROOT,file.username) + if not os.path.exists(archive_dir): + os.makedirs(archive_dir) + + shutil.copyfile(settings.MEDIA_ROOT+file.file.name, os.path.join(archive_dir,file.slug)) + + file.delete() + + messages.info(request, file.slug + " Archived Successfully") + + except shutil.Error: + messages.error(request, "File Copy error, please contact the webmaster") + except : + messages.error(request, "There was an error, please contact the webmaster") + +make_archive.short_description = "Archive selected files" + + +def file_size(obj): + try: + if obj.file.size < 1000000: + return ("%s bytes" % (obj.file.size)) + return ("%s MBs" % (obj.file.size/1000000)) + except OSError: + return ("File Not found") + +file_size.short_description = 'Size' + +# deprecated +def last_modified(obj): + date_text = "File Not Found" + filepath = settings.MEDIA_ROOT+obj.file.name + if os.path.exists(filepath): + date_text = "%s" % dt.datetime.fromtimestamp(os.path.getmtime(filepath)) + + return date_text + +class FileAdmin(admin.ModelAdmin): + list_display = ('slug','username', 'date', file_size) + list_filter = ('username',) + search_fields = ['slug', 'username', 'date'] + ordering = ('username','slug', 'date') + actions = [make_download,make_archive] + + +admin.site.register(File,FileAdmin) diff --git a/fileupload/migrations/0001_initial.py b/fileupload/migrations/0001_initial.py new file mode 100644 index 0000000..3d87f52 --- /dev/null +++ b/fileupload/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'File' + db.create_table(u'fileupload_file', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('file', self.gf('django.db.models.fields.files.FileField')(max_length=100)), + ('slug', self.gf('django.db.models.fields.SlugField')(max_length=50, blank=True)), + ('username', self.gf('django.db.models.fields.CharField')(max_length=50)), + )) + db.send_create_signal(u'fileupload', ['File']) + + + def backwards(self, orm): + # Deleting model 'File' + db.delete_table(u'fileupload_file') + + + models = { + u'fileupload.file': { + 'Meta': {'object_name': 'File'}, + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + } + } + + complete_apps = ['fileupload'] \ No newline at end of file diff --git a/fileupload/migrations/0002_auto__add_field_file_date.py b/fileupload/migrations/0002_auto__add_field_file_date.py new file mode 100644 index 0000000..45648fa --- /dev/null +++ b/fileupload/migrations/0002_auto__add_field_file_date.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding field 'File.date' + db.add_column(u'fileupload_file', 'date', + self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Deleting field 'File.date' + db.delete_column(u'fileupload_file', 'date') + + + models = { + u'fileupload.file': { + 'Meta': {'object_name': 'File'}, + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + } + } + + complete_apps = ['fileupload'] \ No newline at end of file diff --git a/fileupload/migrations/0003_auto__chg_field_file_date.py b/fileupload/migrations/0003_auto__chg_field_file_date.py new file mode 100644 index 0000000..24d9c92 --- /dev/null +++ b/fileupload/migrations/0003_auto__chg_field_file_date.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'File.date' + db.alter_column(u'fileupload_file', 'date', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True)) + + def backwards(self, orm): + + # Changing field 'File.date' + db.alter_column(u'fileupload_file', 'date', self.gf('django.db.models.fields.DateTimeField')()) + + models = { + u'fileupload.file': { + 'Meta': {'object_name': 'File'}, + 'date': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + } + } + + complete_apps = ['fileupload'] \ No newline at end of file diff --git a/fileupload/migrations/__init__.py b/fileupload/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/fileupload/models.py b/fileupload/models.py index f28d242..aff009f 100644 --- a/fileupload/models.py +++ b/fileupload/models.py @@ -1,16 +1,20 @@ # encoding: utf-8 +from datetime import datetime from django.db import models +# Receive the pre_delete signal and delete the file associated with the model instance. +from django.db.models.signals import post_delete +from django.dispatch.dispatcher import receiver -class Picture(models.Model): - """This is a small demo using just two fields. The slug field is really not - necessary, but makes the code simpler. ImageField depends on PIL or - pillow (where Pillow is easily installable in a virtualenv. If you have - problems installing pillow, use a more generic FileField instead. - """ - file = models.ImageField(upload_to="pictures") - slug = models.SlugField(max_length=50, blank=True) +def upload_dir_path(instance, filename): + return 'uploaded_files/%s/%s' % (instance.username, filename) + +class File(models.Model): + file = models.FileField(upload_to=upload_dir_path) + date = models.DateTimeField(auto_now_add=True, blank=True) + slug = models.SlugField(max_length=50, blank=True, verbose_name="file name") + username = models.CharField(max_length=50) def __unicode__(self): return self.file.name @@ -21,9 +25,15 @@ def get_absolute_url(self): def save(self, *args, **kwargs): self.slug = self.file.name - super(Picture, self).save(*args, **kwargs) + super(File, self).save(*args, **kwargs) + + # use post_delete signal instead + #def delete(self, *args, **kwargs): + # """delete -- Remove to leave file.""" + # self.file.delete(False) + # super(File, self).delete(*args, **kwargs) - def delete(self, *args, **kwargs): - """delete -- Remove to leave file.""" - self.file.delete(False) - super(Picture, self).delete(*args, **kwargs) +@receiver(post_delete, sender=File) +def File_delete(sender, instance, **kwargs): + # Pass false so FileField doesn't save the model. + instance.file.delete(False) diff --git a/fileupload/serialize.py b/fileupload/serialize.py index 4494e79..868a17d 100644 --- a/fileupload/serialize.py +++ b/fileupload/serialize.py @@ -12,15 +12,15 @@ def order_name(name): """ name = re.sub(r'^.*/', '', name) - if len(name) <= 20: + if len(name) <= 37: return name - return name[:10] + "..." + name[-7:] + return name[:37] + "..." + name[-7:] def serialize(instance, file_attr='file'): - """serialize -- Serialize a Picture instance into a dict. + """serialize -- Serialize a File instance into a dict. - instance -- Picture instance + instance -- File instance file_attr -- attribute name that contains the FileField or ImageField """ @@ -28,7 +28,8 @@ def serialize(instance, file_attr='file'): return { 'url': obj.url, 'name': order_name(obj.name), - 'type': mimetypes.guess_type(obj.path)[0] or 'image/png', + #'type': mimetypes.guess_type(obj.path)[0] or 'image/png', + 'type': mimetypes.guess_type(obj.path)[0], 'thumbnailUrl': obj.url, 'size': obj.size, 'deleteUrl': reverse('upload-delete', args=[instance.pk]), diff --git a/fileupload/static/css/demo.css b/fileupload/static/css/demo.css index 841f80d..d5d5b0e 100644 --- a/fileupload/static/css/demo.css +++ b/fileupload/static/css/demo.css @@ -15,10 +15,11 @@ body { margin: 0 auto; padding: 1em; font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, sans-serif; - font-size: 1em; - line-height: 1.4em; - background: #222; - color: #fff; + font-size: 10pt; + font-weight: bold; + line-height: 0em; + background: #E2E2E2; + color: #333333; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } @@ -40,9 +41,25 @@ blockquote { } table { width: 100%; - margin: 10px 0; + margin: 10px 10; } + +table td +{ + padding-right: 5px; +} + +.mdtitle h2{ + font-size: 17px; +} + +.mdtitle table{ + padding: 2px; +} + + + .fileupload-progress { margin: 10px 0; } diff --git a/fileupload/static/css/jquery.fileupload-ui.css b/fileupload/static/css/jquery.fileupload-ui.css index f81b34c..760f027 100644 --- a/fileupload/static/css/jquery.fileupload-ui.css +++ b/fileupload/static/css/jquery.fileupload-ui.css @@ -35,6 +35,51 @@ background: url(../img/progressbar.gif) !important; filter: none; } + + +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + background: none repeat scroll 0 0 #005491; + border: 1px solid #515091; + color: #EEEEEE; + font-weight: bold; + margin: 0px; +} + +.ui-widget-content { + border: 1px solid #555555; + height: 14px; + margin-bottom: 10px; + width: 80%; + background: #000000 url(../img/progressbar.gif) 50% 50% repeat; + color: #ffffff; +} + +.ui-widget-header { +border: 1px solid #798E9B; +/*background: #388532 url(images/ui-bg_highlight-soft_44_444444_1x100.png) 50% 50% repeat-x;*/ +background: #005491 url(../img/ui-bg_highlight-soft_44_444444_1x100.png) 50% 50% repeat-x; +color: #ffffff; +font-weight: bold; +} + +.ui-widget { + font-size: 0.92em; +} + +.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { + border-bottom-right-radius: 6px; +} +.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { + border-bottom-left-radius: 6px; +} + +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { + border-top-right-radius: 6px; +} +.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { + border-top-left-radius: 6px; +} + .fileupload-loading { float: right; width: 32px; diff --git a/fileupload/static/img/MD_logo.png b/fileupload/static/img/MD_logo.png new file mode 100644 index 0000000..47b3350 Binary files /dev/null and b/fileupload/static/img/MD_logo.png differ diff --git a/fileupload/static/img/ui-bg_highlight-soft_44_444444_1x100.png b/fileupload/static/img/ui-bg_highlight-soft_44_444444_1x100.png new file mode 100644 index 0000000..32733d6 Binary files /dev/null and b/fileupload/static/img/ui-bg_highlight-soft_44_444444_1x100.png differ diff --git a/fileupload/static/img/ui-bg_highlight-soft_44_444444_1x100.png.orig b/fileupload/static/img/ui-bg_highlight-soft_44_444444_1x100.png.orig new file mode 100644 index 0000000..a4e5b22 Binary files /dev/null and b/fileupload/static/img/ui-bg_highlight-soft_44_444444_1x100.png.orig differ diff --git a/fileupload/static/js/app.js b/fileupload/static/js/app.js index 729eb1a..404b13d 100644 --- a/fileupload/static/js/app.js +++ b/fileupload/static/js/app.js @@ -40,7 +40,7 @@ // send Blob objects via XHR requests: disableImageResize: /Android(?!.*Chrome)|Opera/ .test(window.navigator.userAgent), - maxFileSize: 5000000, + maxFileSize: 100000, acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i }); } diff --git a/fileupload/static/js/jquery.fileupload-validate.js b/fileupload/static/js/jquery.fileupload-validate.js index ee1c2f2..de37447 100644 --- a/fileupload/static/js/jquery.fileupload-validate.js +++ b/fileupload/static/js/jquery.fileupload-validate.js @@ -71,7 +71,7 @@ maxNumberOfFiles: 'Maximum number of files exceeded', acceptFileTypes: 'File type not allowed', maxFileSize: 'File is too large', - minFileSize: 'File is too small' + minFileSize: 'File is too small, min allowed size is 0.1MB.' } }, diff --git a/fileupload/static/js/locale.js b/fileupload/static/js/locale.js index ea64b0a..3375080 100644 --- a/fileupload/static/js/locale.js +++ b/fileupload/static/js/locale.js @@ -15,7 +15,7 @@ window.locale = { "fileupload": { "errors": { "maxFileSize": "File is too big", - "minFileSize": "File is too small", + "minFileSize": "File is too small, minumum size is 5MB.", "acceptFileTypes": "Filetype not allowed", "maxNumberOfFiles": "Max number of files exceeded", "uploadedBytes": "Uploaded bytes exceed file size", diff --git a/fileupload/static/js/main.js b/fileupload/static/js/main.js index 1850909..3a2add2 100644 --- a/fileupload/static/js/main.js +++ b/fileupload/static/js/main.js @@ -17,6 +17,8 @@ $(function () { // Initialize the jQuery File Upload widget: $('#fileupload').fileupload({ + + minFileSize: 100000, // Uncomment the following to send cross-domain cookies: //xhrFields: {withCredentials: true}, //url: 'server/php/' @@ -32,46 +34,20 @@ $(function () { ) ); - if (window.location.hostname === 'blueimp.github.io') { - // Demo settings: - $('#fileupload').fileupload('option', { - url: '//jquery-file-upload.appspot.com/', - // Enable image resizing, except for Android and Opera, - // which actually support image resizing, but fail to - // send Blob objects via XHR requests: - disableImageResize: /Android(?!.*Chrome)|Opera/ - .test(window.navigator.userAgent), - maxFileSize: 5000000, - acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i - }); - // Upload server status check for browsers with CORS support: - if ($.support.cors) { - $.ajax({ - url: '//jquery-file-upload.appspot.com/', - type: 'HEAD' - }).fail(function () { - $('
') - .text('Upload server currently unavailable - ' + - new Date()) - .appendTo('#fileupload'); - }); - } - } else { - // Load existing files: - $('#fileupload').addClass('fileupload-processing'); - $.ajax({ - // Uncomment the following to send cross-domain cookies: - //xhrFields: {withCredentials: true}, - //url: $('#fileupload').fileupload('option', 'url'), - url: '/upload/view/', - dataType: 'json', - context: $('#fileupload')[0] - }).always(function () { - $(this).removeClass('fileupload-processing'); - }).done(function (result) { - $(this).fileupload('option', 'done') - .call(this, null, {result: result}); - }); - } + // Load existing files: + $('#fileupload').addClass('fileupload-processing'); + $.ajax({ + // Uncomment the following to send cross-domain cookies: + //xhrFields: {withCredentials: true}, + //url: $('#fileupload').fileupload('option', 'url'), + url: 'https://upload.moldiscovery.com/secureupload/upload/view/', + dataType: 'json', + context: $('#fileupload')[0] + }).always(function () { + $(this).removeClass('fileupload-processing'); + }).done(function (result) { + $(this).fileupload('option', 'done') + .call(this, null, {result: result}); + }); }); diff --git a/fileupload/templates/fileupload/file_jquery_form.html b/fileupload/templates/fileupload/file_jquery_form.html new file mode 100644 index 0000000..e1d5c68 --- /dev/null +++ b/fileupload/templates/fileupload/file_jquery_form.html @@ -0,0 +1,269 @@ + + + + + + + +Molecular Discovery Secure File Upload + + + + + + + + + + + + + + + + +{% block contents %} +{% if user.is_authenticated %} +
+ + + + + +

Molecular Discovery Secure File Upload.

+

MAX aggregated files upload size: 4 GB

+

TIP: compress or chunck big files before uploading

+
+ + + +
{% csrf_token %} + + + +
+
+ + + Add files... + + + + + {% if perms.fileupload %} + + + + + + {% endif %} + + + + +
+ + +
+ + +
+
+ + + +
+{% else %} +
+

Molecular Discovery Secure Upload Login.

+

+ Please use the login credential that our staff provided you:
+

+
+ {% csrf_token %} +
+ + +
+
+ + +

+ +

+
+{% endif %} +{% endblock %} + +
+ +{% verbatim %} + + +{% endverbatim %} + +{% if perms.fileupload %} + +{% verbatim %} + + +{% endverbatim %} + +{% else %} + +{% verbatim %} + + +{% endverbatim %} + + +{% endif %} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/fileupload/templates/fileupload/picture_angular_form.html b/fileupload/templates/fileupload/picture_angular_form.html deleted file mode 100644 index a5ac9c0..0000000 --- a/fileupload/templates/fileupload/picture_angular_form.html +++ /dev/null @@ -1,207 +0,0 @@ - - - - - - - -jQuery File Upload Demo - AngularJS version - - - - - - - - - - - - - - - - -
-

Django jQuery File Upload Demo

-

AngularJS version

- -
-
-

File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for AngularJS.
- Supports cross-domain, chunked and resumable file uploads and client-side image resizing.

-
-
- -
{% csrf_token %} - - - -
-
- - - - Add files... - - - - - -
-
- -
- -
- -
 
-
-
- {% verbatim %} - - - - - - - - -
-
- -
-
-
-

- - {{file.name}} - {{file.name}} - - {{file.name}} -

-
Error {{file.error}}
-
-

{{file.size | formatFileSize}}

-
-
- - - -
- {% endverbatim %} -
-
-
-
-

Demo Notes

-
-
- -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fileupload/templates/fileupload/picture_basic_form.html b/fileupload/templates/fileupload/picture_basic_form.html deleted file mode 100644 index 6bd17a4..0000000 --- a/fileupload/templates/fileupload/picture_basic_form.html +++ /dev/null @@ -1,130 +0,0 @@ - - - - - - -Django jQuery File Upload Demo - Basic version - - - - - - - - - - - -
-

Django jQuery File Upload Demo

-

Basic version

- -
-
-

File Upload widget with multiple file selection, drag&drop support and progress bar for jQuery.
- Supports cross-domain, chunked and resumable file uploads.

-
-
- - - - Select files... - - - -
-
- -
-
-
- -
-
-
-
-

Demo Notes

-
-
- -
-
-
- - - - - - - - - - - - - diff --git a/fileupload/templates/fileupload/picture_basicplus_form.html b/fileupload/templates/fileupload/picture_basicplus_form.html deleted file mode 100644 index eb1cc1c..0000000 --- a/fileupload/templates/fileupload/picture_basicplus_form.html +++ /dev/null @@ -1,221 +0,0 @@ - - - - - - -jQuery File Upload Demo - Basic Plus version - - - - - - - - - - - -
-

Django jQuery File Upload Demo

-

Basic Plus version

- -
-
-

File Upload widget with multiple file selection, drag&drop support, progress bar, validation and preview images, audio and video for jQuery.
- Supports cross-domain, chunked and resumable file uploads and client-side image resizing.

-
-
- - - - Add files... - - - -
-
- -
-
-
- -
-
-
-
-

Demo Notes

-
-
-
    -
  • The maximum file size for uploads in this demo is 5 MB (default file size is unlimited).
  • -
  • Only image files (JPG, GIF, PNG) are allowed in this demo (by default there is no file type restriction).
  • -
  • You can drag & drop files from your desktop on this webpage (see Browser support).
  • -
  • Please refer to the project website and documentation for more information.
  • -
  • Built with Twitter's Bootstrap CSS framework and Icons from Glyphicons.
  • -
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fileupload/templates/fileupload/picture_form.html b/fileupload/templates/fileupload/picture_form.html deleted file mode 100644 index a8754bc..0000000 --- a/fileupload/templates/fileupload/picture_form.html +++ /dev/null @@ -1,126 +0,0 @@ -{% extends "upload_base.html" %} -{% load upload_tags %} - -{% block content %} -
-

Django jQuery File Upload Demo

-

Basic Plus UI version

- -
-
-

File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery.
- Supports cross-domain, chunked and resumable file uploads and client-side image resizing.

-
-
- -
{% csrf_token %} - - - -
-
- - - - Add files... - - - - - - - - -
- -
- -
-
-
- -
 
-
-
- - -
-
-
-
-

Demo Notes

-
-
- -
-
-
- - -{% upload_js %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{% endblock %} \ No newline at end of file diff --git a/fileupload/templates/fileupload/picture_jquery_form.html b/fileupload/templates/fileupload/picture_jquery_form.html deleted file mode 100644 index cf0ed04..0000000 --- a/fileupload/templates/fileupload/picture_jquery_form.html +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - - -Django jQuery File Upload Demo - jQuery UI version - - - - - - - - - - - - - - - - - -

Django jQuery File Upload Demo

-

jQuery UI version

-
- - -
- -
-

File Upload widget with multiple file selection, drag&drop support, progress bars, validation and preview images, audio and video for jQuery UI.
- Supports cross-domain, chunked and resumable file uploads and client-side image resizing.

-
- -
{% csrf_token %} - - - -
-
- - - Add files... - - - - - - - - -
- - -
- -
-
-
-

Demo Notes

- - - -{% verbatim %} - - - - -{% endverbatim %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/fileupload/templates/upload_base.html b/fileupload/templates/upload_base.html deleted file mode 100644 index e75c3fc..0000000 --- a/fileupload/templates/upload_base.html +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - Django Jquery file upload demo - - - - - - - - - - - - - - - - - - - -{% block content %} -

No content set

-{% endblock %} - - - diff --git a/fileupload/urls.py b/fileupload/urls.py index dbbcbca..963aa68 100644 --- a/fileupload/urls.py +++ b/fileupload/urls.py @@ -1,17 +1,16 @@ # encoding: utf-8 from django.conf.urls import patterns, url + from fileupload.views import ( - BasicVersionCreateView, BasicPlusVersionCreateView, - jQueryVersionCreateView, AngularVersionCreateView, - PictureCreateView, PictureDeleteView, PictureListView, + jQueryVersionCreateView, + FileCreateView, FileDeleteView, FileListView, + login_view, logout_view, ) urlpatterns = patterns('', - url(r'^basic/$', BasicVersionCreateView.as_view(), name='upload-basic'), - url(r'^basic/plus/$', BasicPlusVersionCreateView.as_view(), name='upload-basic-plus'), - url(r'^new/$', PictureCreateView.as_view(), name='upload-new'), - url(r'^angular/$', AngularVersionCreateView.as_view(), name='upload-angular'), - url(r'^jquery-ui/$', jQueryVersionCreateView.as_view(), name='upload-jquery'), - url(r'^delete/(?P\d+)$', PictureDeleteView.as_view(), name='upload-delete'), - url(r'^view/$', PictureListView.as_view(), name='upload-view'), + url(r'^login/$', login_view, name='login'), + url(r'^logout/$', logout_view, name='logout'), + url(r'^home/$', jQueryVersionCreateView.as_view(), name='upload-jquery'), + url(r'^delete/(?P\d+)$', FileDeleteView.as_view(), name='upload-delete'), + url(r'^view/$', FileListView.as_view(), name='upload-view'), ) diff --git a/fileupload/views.py b/fileupload/views.py index 98b339c..0be5568 100644 --- a/fileupload/views.py +++ b/fileupload/views.py @@ -3,46 +3,87 @@ from django.http import HttpResponse from django.views.generic import CreateView, DeleteView, ListView -from .models import Picture +from .models import File from .response import JSONResponse, response_mimetype from .serialize import serialize - - -class PictureCreateView(CreateView): - model = Picture +from django.contrib.auth import authenticate, login +from django.contrib.auth import logout +from django.shortcuts import redirect +import smtplib + +def send_email(response,user): + + FROMADDR = "uploads@www2.moldiscovery.com" + LOGIN = FROMADDR + #PASSWORD = "" + TOADDRS = ["fabrizio@moldiscovery.com"] + SUBJECT = "Files uploaded" + msg_content = "User: %s \r\n" % user + + for file in response['files']: + msg_content += "File Uploaded: "+ file['name'] + '\r\n' + msg_content += "File Size: "+ str(file['size']) + ' bytes\r\n' + msg_content += "Download: https://www2.moldiscovery.com/secureupload/admin/" + '\r\n\r\n' + + msg = ("From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n" + % (FROMADDR, ", ".join(TOADDRS), SUBJECT) ) + msg += msg_content+"\r\n" + + server = smtplib.SMTP('localhost', 25) + #server.set_debuglevel(1) + server.ehlo("www2.moldiscovery.com") + #server.starttls() + #server.login(LOGIN, PASSWORD) + server.sendmail(FROMADDR, TOADDRS, msg) + server.quit() + + +def logout_view(request): + logout(request) + return redirect('home') + +def login_view(request): + + username = request.POST['username'] + password = request.POST['password'] + user = authenticate(username=username, password=password) + if user is not None: + if user.is_active: + login(request, user) + + return redirect('home') + +class FileCreateView(CreateView): + model = File def form_valid(self, form): + if not self.request.user.is_authenticated(): + return redirect('home') + self.object = form.save() files = [serialize(self.object)] data = {'files': files} response = JSONResponse(data, mimetype=response_mimetype(self.request)) response['Content-Disposition'] = 'inline; filename=files.json' + #send_email(data, self.request.user.username) return response def form_invalid(self, form): data = json.dumps(form.errors) return HttpResponse(content=data, status=400, content_type='application/json') -class BasicVersionCreateView(PictureCreateView): - template_name_suffix = '_basic_form' - - -class BasicPlusVersionCreateView(PictureCreateView): - template_name_suffix = '_basicplus_form' - -class AngularVersionCreateView(PictureCreateView): - template_name_suffix = '_angular_form' - - -class jQueryVersionCreateView(PictureCreateView): +class jQueryVersionCreateView(FileCreateView): template_name_suffix = '_jquery_form' -class PictureDeleteView(DeleteView): - model = Picture +class FileDeleteView(DeleteView): + model = File def delete(self, request, *args, **kwargs): + if not self.request.user.is_authenticated(): + return redirect('home') + self.object = self.get_object() self.object.delete() response = JSONResponse(True, mimetype=response_mimetype(request)) @@ -50,10 +91,17 @@ def delete(self, request, *args, **kwargs): return response -class PictureListView(ListView): - model = Picture +class FileListView(ListView): + model = File + + def get_queryset(self): + return File.objects.filter(username=self.request.user) def render_to_response(self, context, **response_kwargs): + + if not self.request.user.is_authenticated(): + return redirect('home') + files = [ serialize(p) for p in self.get_queryset() ] data = {'files': files} response = JSONResponse(data, mimetype=response_mimetype(self.request)) diff --git a/manage.py b/manage.py index 3e4eedc..f9726f9 100755 --- a/manage.py +++ b/manage.py @@ -1,14 +1,10 @@ #!/usr/bin/env python -from django.core.management import execute_manager -import imp -try: - imp.find_module('settings') # Assumed to be in the same directory. -except ImportError: - import sys - sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n" % __file__) - sys.exit(1) - -import settings +import os +import sys if __name__ == "__main__": - execute_manager(settings) + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings") + + from django.core.management import execute_from_command_line + + execute_from_command_line(sys.argv) diff --git a/requirements.txt b/requirements.txt index 23cb63a..8790390 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ -django<1.6 -pillow +django==1.5.12 +pillow==6.2.2 +South==1.0.2 diff --git a/settings.py b/settings.py index 68744b1..7b0832d 100644 --- a/settings.py +++ b/settings.py @@ -1,12 +1,16 @@ import os -DEBUG = True +DEBUG = False TEMPLATE_DEBUG = DEBUG +ALLOWED_HOSTS = ['upload.moldiscovery.com','www2.moldiscovery.com','localhost','127.0.0.1','ajax.googleapis.com'] + SITE_ROOT = os.path.dirname(os.path.realpath(__file__)) +SERVER_EMAIL = 'admin@www2.moldiscovery.com' + ADMINS = ( - # ('Your Name', 'your_email@example.com'), + ('SysAdmin', 'fabrizio@moldiscovery.com'), ) MANAGERS = ADMINS @@ -29,7 +33,7 @@ # timezone as the operating system. # If running in a Windows environment this must be set to the same as your # system time zone. -TIME_ZONE = 'America/Chicago' +TIME_ZONE = 'Europe/Rome' # Language code for this installation. All choices can be found here: # http://www.i18nguy.com/unicode/language-identifiers.html @@ -47,13 +51,16 @@ # Absolute filesystem path to the directory that will hold user-uploaded files. # Example: "/home/media/media.lawrence.com/media/" -MEDIA_ROOT = os.path.abspath(os.path.dirname(__file__)) + '/media/' +#MEDIA_ROOT = os.path.abspath(os.path.dirname(__file__)) + '/media/' +MEDIA_ROOT = '/var/media-storage/' # URL that handles the media served from MEDIA_ROOT. Make sure to use a # trailing slash. # Examples: "http://media.lawrence.com/media/", "http://example.com/media/" MEDIA_URL = '/media/' +ARCHIVE_ROOT = os.path.join(MEDIA_ROOT,'archived') + # Absolute path to the directory static files should be collected to. # Don't put anything in this directory yourself; store your static files # in apps' "static/" subdirectories and in STATICFILES_DIRS. @@ -102,7 +109,7 @@ 'django.contrib.messages.middleware.MessageMiddleware', ) -ROOT_URLCONF = 'django-jquery-file-upload.urls' +ROOT_URLCONF = 'urls' TEMPLATE_DIRS = ( # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". @@ -133,11 +140,17 @@ 'version': 1, 'disable_existing_loggers': False, 'handlers': { - 'mail_admins': { - 'level': 'ERROR', - 'class': 'django.utils.log.AdminEmailHandler' + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'django.utils.log.AdminEmailHandler' } }, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, 'loggers': { 'django.request': { 'handlers': ['mail_admins'], diff --git a/settings_dev.py b/settings_dev.py new file mode 100644 index 0000000..4806e13 --- /dev/null +++ b/settings_dev.py @@ -0,0 +1,160 @@ +import os + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +# uncomment this on production server and DEBUG=False +#ALLOWED_HOSTS = ['ml.moldiscovery.com','ajax.googleapis.com'] + +SITE_ROOT = os.path.dirname(os.path.realpath(__file__)) + +ADMINS = ( + ('Fabrizio Buratta', 'fabrizio@moldiscovery.com'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. + 'NAME': os.path.join(SITE_ROOT, 'db'), # Or path to database file if using sqlite3. + 'USER': '', # Not used with sqlite3. + 'PASSWORD': '', # Not used with sqlite3. + 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. + 'PORT': '', # Set to empty string for default. Not used with sqlite3. + } +} + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# On Unix systems, a value of None will cause Django to use the same +# timezone as the operating system. +# If running in a Windows environment this must be set to the same as your +# system time zone. +TIME_ZONE = 'Europe/Rome' + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = 'en-us' + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale +USE_L10N = True + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/home/media/media.lawrence.com/media/" +MEDIA_ROOT = os.path.abspath(os.path.dirname(__file__)) + '/media/' + +ARCHIVE_ROOT = os.path.join(MEDIA_ROOT,'archived') + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" +MEDIA_URL = '/media/' + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/home/media/media.lawrence.com/static/" +STATIC_ROOT = os.path.abspath(os.path.dirname(__file__)) + '/static/' + +# URL prefix for static files. +# Example: "http://media.lawrence.com/static/" +STATIC_URL = '/static/' + +# URL prefix for admin static files -- CSS, JavaScript and images. +# Make sure to use a trailing slash. +# Examples: "http://foo.com/static/admin/", "/static/admin/". +ADMIN_MEDIA_PREFIX = '/static/admin/' + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + 'django.contrib.staticfiles.finders.FileSystemFinder', + 'django.contrib.staticfiles.finders.AppDirectoriesFinder', +# 'django.contrib.staticfiles.finders.DefaultStorageFinder', +) + +# Make this unique, and don't share it with anybody. +SECRET_KEY = '9%$in^gpdaig@v3or_to&_z(=n)3)$f1mr3hf9e#kespy2ajlo' + +# List of callables that know how to import templates from various sources. +TEMPLATE_LOADERS = ( + 'django.template.loaders.filesystem.Loader', + 'django.template.loaders.app_directories.Loader', +# 'django.template.loaders.eggs.Loader', +) + +MIDDLEWARE_CLASSES = ( + 'django.middleware.common.CommonMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', +) + +ROOT_URLCONF = 'urls' + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +INSTALLED_APPS = ( + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.sites', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'fileupload', + # Uncomment the next line to enable the admin: + 'django.contrib.admin', + 'south' + # Uncomment the next line to enable admin documentation: + # 'django.contrib.admindocs', +) + +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error. +# See http://docs.djangoproject.com/en/dev/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'handlers': { + 'mail_admins': { + 'level': 'ERROR', + 'filters': ['require_debug_false'], + 'class': 'django.utils.log.AdminEmailHandler' + } + }, + 'filters': { + 'require_debug_false': { + '()': 'django.utils.log.RequireDebugFalse' + } + }, + 'loggers': { + 'django.request': { + 'handlers': ['mail_admins'], + 'level': 'ERROR', + 'propagate': True, + }, + } +} diff --git a/static/css/jquery.fileupload-ui.css b/static/css/jquery.fileupload-ui.css new file mode 100644 index 0000000..6d96259 --- /dev/null +++ b/static/css/jquery.fileupload-ui.css @@ -0,0 +1,114 @@ +@charset "UTF-8"; +/* + * jQuery File Upload UI Plugin CSS 8.8.1 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2010, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +.fileinput-button { + position: relative; + overflow: hidden; +} +.fileinput-button input { + position: absolute; + top: 0; + right: 0; + margin: 0; + opacity: 0; + filter: alpha(opacity=0); + transform: translate(-300px, 0) scale(4); + font-size: 23px; + direction: ltr; + cursor: pointer; +} +.fileupload-buttonbar .btn, +.fileupload-buttonbar .toggle { + margin-bottom: 5px; +} +.progress-animated .progress-bar, +.progress-animated .bar { + background: url(../img/progressbar.gif) !important; + filter: none; +} + + +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { + background: none repeat scroll 0 0 #005491; + border: 1px solid #515091; + color: #EEEEEE; + font-weight: bold; + margin: 0px; +} + +.ui-widget-content { + border: 1px solid #555555; + height: 14px; + margin-bottom: 10px; + width: 80%; + background: #000000 url(../img/progressbar.gif) 50% 50% repeat; + color: #ffffff; +} + +.ui-widget-header { +border: 1px solid #798E9B; +/*background: #388532 url(images/ui-bg_highlight-soft_44_444444_1x100.png) 50% 50% repeat-x;*/ +background: #005491 url(../img/ui-bg_highlight-soft_44_444444_1x100.png) 50% 50% repeat-x; +color: #ffffff; +font-weight: bold; +} + +.ui-widget { + font-size: 0.92em; +} + +.ui-corner-all, .ui-corner-bottom, .ui-corner-right, .ui-corner-br { + border-bottom-right-radius: 6px; +} +.ui-corner-all, .ui-corner-bottom, .ui-corner-left, .ui-corner-bl { + border-bottom-left-radius: 6px; +} + +.ui-corner-all, .ui-corner-top, .ui-corner-right, .ui-corner-tr { + border-top-right-radius: 6px; +} +.ui-corner-all, .ui-corner-top, .ui-corner-left, .ui-corner-tl { + border-top-left-radius: 6px; +} + +.fileupload-loading { + float: right; + width: 32px; + height: 32px; + background: url(../img/loading.gif) center no-repeat; + background-size: contain; + display: none; +} + +.fileupload-processing .fileupload-loading { + display: block; +} +.files audio, +.files video { + max-width: 300px; +} + +@media (max-width: 767px) { + .fileupload-buttonbar .toggle, + .files .toggle, + .files .btn span { + display: none; + } + .files .name { + width: 80px; + word-wrap: break-word; + } + .files audio, + .files video { + max-width: 80px; + } +} diff --git a/urls.py b/urls.py index 00270ad..e7ae398 100644 --- a/urls.py +++ b/urls.py @@ -9,7 +9,7 @@ # Examples: # url(r'^$', 'upload.views.home', name='home'), - url(r'^$', lambda x: HttpResponseRedirect('/upload/new/')), + url(r'^$', lambda x: HttpResponseRedirect('upload/home'), name='home'), url(r'^upload/', include('fileupload.urls')), # Uncomment the admin/doc line below to enable admin documentation: diff --git a/utils/update_datefield.py b/utils/update_datefield.py new file mode 100644 index 0000000..b894412 --- /dev/null +++ b/utils/update_datefield.py @@ -0,0 +1,41 @@ +# This script can be invoked from python manage.py shell. It's aim is to update the Date Field of the fileupload model +# with the dates of existing files in the media storage and associated to the model object + +import datetime as dt +import os +from fileupload.models import File +from django.conf import settings + + +def sync_slug(allobj): + """ + fill slug field from file name when missing + """ + for obj in allobj: + obj.slug = os.path.basename(obj.file.name) + obj.save() + print("fix missing slug: %s") % obj.slug + +db_files = File.objects.all() +uploads_path = os.path.join(settings.MEDIA_ROOT,'uploaded_files') +users = set() + +sync_slug(db_files) + +for f in db_files: + users.add(f.username) + +for user_dir in users: + user_files = os.listdir(os.path.join(uploads_path, user_dir)) + print(user_dir + " " + str(len(user_files))) + for fname in user_files: + # only if there are duplicated + print("processa "+ " dir: "+user_dir +" name: " + fname) + qset = File.objects.filter(username=user_dir, slug=fname) + if not qset: + print("ERROR: file %s/%s not found") % (user_dir, fname) + if len(qset) > 1: + print("%s/%s is duplicated") % (user_dir, fname) + obj = qset[0] + obj.date = dt.datetime.fromtimestamp(os.path.getmtime(os.path.join(uploads_path, user_dir,obj.slug))) + obj.save() \ No newline at end of file