Skip to content

Commit ef818ef

Browse files
committed
first commit
0 parents  commit ef818ef

File tree

7 files changed

+396
-0
lines changed

7 files changed

+396
-0
lines changed

.gitignore

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
5+
# C extensions
6+
*.so
7+
8+
# Distribution / packaging
9+
.Python
10+
env/
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
*.egg-info/
23+
.installed.cfg
24+
*.egg
25+
26+
# PyInstaller
27+
# Usually these files are written by a python script from a template
28+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
29+
*.manifest
30+
*.spec
31+
32+
# Installer logs
33+
pip-log.txt
34+
pip-delete-this-directory.txt
35+
36+
# Unit test / coverage reports
37+
htmlcov/
38+
.tox/
39+
.coverage
40+
.coverage.*
41+
.cache
42+
nosetests.xml
43+
coverage.xml
44+
*,cover
45+
46+
# Translations
47+
*.mo
48+
*.pot
49+
50+
# Django stuff:
51+
*.log
52+
53+
# Sphinx documentation
54+
docs/_build/
55+
56+
# PyBuilder
57+
target/
58+
59+
# IDE/Editor stuff
60+
.idea/*

LICENCE

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
django-filebrowser-browse-and-upload-field
2+
------------------
3+
4+
Copyright (c) Patrick Kranzlmueller, Axel Swoboda (vonautomatisch werkstaetten),
5+
All rights reserved.
6+
7+
Redistribution and use in source and binary forms, with or without modification,
8+
are permitted provided that the following conditions are met:
9+
10+
1. Redistributions of source code must retain the above copyright notice,
11+
this list of conditions and the following disclaimer.
12+
2. Redistributions in binary form must reproduce the above copyright notice,
13+
this list of conditions and the following disclaimer in the documentation and/or
14+
other materials provided with the distribution.
15+
3. Neither the name of FileBrowser nor the names of its contributors may be used
16+
to endorse or promote products derived from this software without specific prior
17+
written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
20+
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21+
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22+
THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23+
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

README.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Django FileBrowser Browse and Upload Field
2+
==================
3+
4+
This field combines the normal FilebrowseField with the direct upload functionality of the UploadField. The widget has both an upload and browse button.
5+
6+
Requirements
7+
------------
8+
9+
* django-filebrowser-no-grappelli 3.6+ ( https://github.com/smacker/django-filebrowser-no-grappelli )
10+
11+
Installation
12+
------------
13+
14+
* add 'browse_and_upload_field' to INSTALLED_APPS
15+
* from browse_and_upload_field.fields import FileBrowseAndUploadField
16+
* Use in the same way as the existing FileBrowseField and UploadField
17+
18+
my_image = FileBrowseAndUploadField(max_length=512, null=True, blank=True, format="image", directory='images', upload_to='images')

browse_and_upload_field/__init__.py

Whitespace-only changes.

browse_and_upload_field/fields.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# coding: utf-8
2+
3+
# PYTHON IMPORTS
4+
import os
5+
6+
# DJANGO IMPORTS
7+
from django.core import urlresolvers
8+
from django.db import models
9+
from django.db.models.fields import CharField
10+
from django import forms
11+
from django.forms.widgets import Input
12+
from django.template.loader import render_to_string
13+
from django.utils.six import with_metaclass
14+
from django.utils.translation import ugettext_lazy as _
15+
from django.contrib.admin.templatetags.admin_static import static
16+
17+
# FILEBROWSER IMPORTS
18+
from filebrowser.settings import UPLOAD_TEMPDIR, ADMIN_THUMBNAIL, EXTENSIONS
19+
from filebrowser.base import FileObject
20+
from filebrowser.sites import site
21+
22+
23+
class FileBrowseAndUploadWidget(Input):
24+
input_type = 'text'
25+
26+
class Media:
27+
js = (
28+
'filebrowser/js/AddFileBrowser.js',
29+
'filebrowser/js/fileuploader.js',
30+
)
31+
css = {
32+
'all': (os.path.join('/static/filebrowser/css/uploadfield.css'),)
33+
}
34+
35+
def __init__(self, attrs=None):
36+
super(FileBrowseAndUploadWidget, self).__init__(attrs)
37+
self.site = attrs.get('filebrowser_site', '')
38+
self.directory = attrs.get('directory', '')
39+
self.extensions = attrs.get('extensions', '')
40+
self.format = attrs.get('format', '')
41+
self.upload_to = attrs.get('upload_to', '')
42+
self.temp_upload_dir = attrs.get('temp_upload_dir', '')
43+
if attrs is not None:
44+
self.attrs = attrs.copy()
45+
else:
46+
self.attrs = {}
47+
super(FileBrowseAndUploadWidget, self).__init__(attrs)
48+
49+
def render(self, name, value, attrs=None):
50+
url = urlresolvers.reverse(self.site.name + ":fb_browse")
51+
if value is None:
52+
value = ""
53+
if value != "" and not isinstance(value, FileObject):
54+
value = FileObject(value, site=self.site)
55+
final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
56+
final_attrs['search_icon'] = static('filebrowser/img/filebrowser_icon_show.gif')
57+
final_attrs['url'] = url
58+
final_attrs['directory'] = self.directory
59+
final_attrs['extensions'] = self.extensions
60+
final_attrs['format'] = self.format
61+
final_attrs['upload_to'] = self.upload_to
62+
final_attrs['temp_upload_dir'] = UPLOAD_TEMPDIR
63+
final_attrs['ADMIN_THUMBNAIL'] = ADMIN_THUMBNAIL
64+
filebrowser_site = self.site
65+
if value != "":
66+
try:
67+
final_attrs['directory'] = os.path.split(value.original.path_relative_directory)[0]
68+
except:
69+
pass
70+
return render_to_string("filebrowser/custom_browse_and_upload_field.html", locals())
71+
72+
73+
class FileBrowseAndUploadFormField(forms.CharField):
74+
75+
default_error_messages = {
76+
'extension': _(u'Extension %(ext)s is not allowed. Only %(allowed)s is allowed.'),
77+
}
78+
79+
def __init__(self, max_length=None, min_length=None, site=None, directory=None, extensions=None, format=None, upload_to=None, temp_upload_dir=None, *args, **kwargs):
80+
self.max_length, self.min_length = max_length, min_length
81+
self.site = kwargs.pop('filebrowser_site', site)
82+
self.directory = directory
83+
self.extensions = extensions
84+
if format:
85+
self.format = format or ''
86+
self.extensions = extensions or EXTENSIONS.get(format)
87+
self.upload_to = upload_to
88+
self.temp_upload_dir = temp_upload_dir
89+
super(FileBrowseAndUploadFormField, self).__init__(*args, **kwargs)
90+
91+
def clean(self, value):
92+
value = super(FileBrowseAndUploadFormField, self).clean(value)
93+
if value == '':
94+
return value
95+
file_extension = os.path.splitext(value)[1].lower()
96+
if self.extensions and file_extension not in self.extensions:
97+
raise forms.ValidationError(self.error_messages['extension'] % {'ext': file_extension, 'allowed': ", ".join(self.extensions)})
98+
return value
99+
100+
101+
class FileBrowseAndUploadField(with_metaclass(models.SubfieldBase, CharField)):
102+
103+
description = "FileBrowseAndUploadField"
104+
105+
def __init__(self, *args, **kwargs):
106+
self.site = kwargs.pop('filebrowser_site', site)
107+
self.directory = kwargs.pop('directory', '')
108+
self.extensions = kwargs.pop('extensions', '')
109+
self.format = kwargs.pop('format', '')
110+
self.upload_to = kwargs.pop('upload_to', '')
111+
self.temp_upload_dir = kwargs.pop('temp_upload_dir', '')
112+
return super(FileBrowseAndUploadField, self).__init__(*args, **kwargs)
113+
114+
def to_python(self, value):
115+
if not value or isinstance(value, FileObject):
116+
return value
117+
return FileObject(value, site=self.site)
118+
119+
def get_db_prep_value(self, value, connection, prepared=False):
120+
if not value:
121+
return value
122+
return value.path
123+
124+
def get_prep_value(self, value):
125+
if not value:
126+
return value
127+
return value.path
128+
129+
def value_to_string(self, obj):
130+
value = self._get_val_from_obj(obj)
131+
if not value:
132+
return value
133+
return value.path
134+
135+
def formfield(self, **kwargs):
136+
attrs = {}
137+
attrs["filebrowser_site"] = self.site
138+
attrs["directory"] = self.directory
139+
attrs["extensions"] = self.extensions
140+
attrs["format"] = self.format
141+
attrs["upload_to"] = self.upload_to
142+
attrs["temp_upload_dir"] = self.temp_upload_dir
143+
defaults = {
144+
'form_class': FileBrowseAndUploadFormField,
145+
'widget': FileBrowseAndUploadWidget(attrs=attrs),
146+
'filebrowser_site': self.site,
147+
'directory': self.directory,
148+
'extensions': self.extensions,
149+
'format': self.format,
150+
'upload_to': self.upload_to,
151+
'temp_upload_dir': self.temp_upload_dir
152+
}
153+
return super(FileBrowseAndUploadField, self).formfield(**defaults)
154+
155+
156+
try:
157+
from south.modelsinspector import add_introspection_rules
158+
add_introspection_rules([], ["^filebrowser\.fields\.FileBrowseAndUploadField"])
159+
except:
160+
pass
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
{% load i18n fb_versions %}
2+
<input id="{{ final_attrs.id }}" type="text" class="vTextField vFileBrowseField vFileBrowseField{% if final_attrs.class %} {{ final_attrs.class }}{% endif %}" name="{{ final_attrs.name }}" value="{{ value.path }}" />
3+
<div id="{{ final_attrs.id }}-uploader" class="fb-uploader-container"></div>
4+
<a href="javascript:FileBrowser.show('{{ final_attrs.id }}', '{{ url }}?pop=1{% if final_attrs.directory %}&amp;dir={{ final_attrs.directory|urlencode|urlencode }}{% endif %}{% if final_attrs.format %}&amp;type={{ final_attrs.format }}{% endif %}');" class="fb_show"></a>
5+
<a href="#" onclick="FileBrowser.show(this.parentNode.getElementsByTagName('input')[0].id, '{{ url }}?pop=1{% if final_attrs.directory %}&amp;dir={{ final_attrs.directory|urlencode|urlencode }}{% endif %}{% if final_attrs.format %}&amp;type={{ final_attrs.format }}{% endif %}');" class="fb_show">
6+
<img src="{{ final_attrs.search_icon }}" alt="{% trans "Change" %}" />
7+
</a>
8+
<div class="fb-uploadfield{% if value and not value.exists %} error{% endif %}">
9+
{% if value and not value.exists %}
10+
<ul class="errorlist"><li>{% trans "File not found" %}</li></ul>
11+
{% endif %}
12+
</div>
13+
{% if value.filetype == "Image" and value.exists %}
14+
{% version_object value.path final_attrs.ADMIN_THUMBNAIL as thumbnail_version %}
15+
{% if thumbnail_version %}
16+
<p class="preview" id="preview_{{ final_attrs.id }}">
17+
<a href="{{ value.url }}" target="_blank" id="previewlink_{{ final_attrs.id }}">
18+
<img id="previewimage_{{ final_attrs.id }}" src="{{ thumbnail_version.url }}" class="preview" />
19+
</a>
20+
</p>
21+
{% else %}
22+
<p class="preview" id="preview_{{ final_attrs.id }}" style="display: none;">
23+
<a href="javascript://" target="_self" id="previewlink_{{ final_attrs.id }}">
24+
<img id="previewimage_{{ final_attrs.id }}" class="preview" src="" />
25+
</a>
26+
</p>
27+
{% endif %}
28+
{% else %}
29+
<p class="preview" id="preview_{{ final_attrs.id }}" style="display: none;">
30+
<a href="javascript://" target="_self" id="previewlink_{{ final_attrs.id }}">
31+
<img id="previewimage_{{ final_attrs.id }}" class="preview" src="" />
32+
</a>
33+
</p>
34+
{% endif %}
35+
{% if value and not value.exists %}
36+
<ul class="errorlist"><li>{% trans "File not found" %}</li></ul>
37+
{% endif %}
38+
<script type="text/javascript">
39+
(function($){
40+
$(document).ready(function() {
41+
var uploader = new qq.FileUploader({
42+
element: $('#{{ final_attrs.id }}-uploader').get(0),
43+
action: '{% url "filebrowser:fb_do_upload" %}',
44+
45+
template: '<div class="fb-uploader">' +
46+
'<div class="fb-upload-list"></div>' +
47+
'<a href="javascript://" class="fb-upload-button" title="{% trans "Upload a file" %}">&nbsp;</a>' +
48+
'<div class="fb-upload-drop-area"><span>Drop files here to upload</span></div>' +
49+
'</div>',
50+
51+
// template for one item in file list
52+
fileTemplate: '<div class="fb-upload-item">' +
53+
'<span class="fb-upload-file"></span>' +
54+
'<span class="fb-upload-spinner">&nbsp;</span>' +
55+
'<span class="fb-upload-size"></span>' +
56+
'<a class="fb-upload-cancel" href="#"></a>' +
57+
'<span class="fb-upload-failed-text"></span>' +
58+
'<div class="progress-bar"><div class="content"></div></div>' +
59+
'</div>',
60+
61+
classes: {
62+
// used to get elements from templates
63+
button: 'fb-upload-button',
64+
drop: 'fb-upload-drop-area',
65+
dropActive: 'fb-upload-drop-area-active',
66+
list: 'fb-upload-list',
67+
68+
file: 'fb-upload-file',
69+
spinner: 'fb-upload-spinner',
70+
size: 'fb-upload-size',
71+
cancel: 'fb-upload-cancel',
72+
73+
// added to list item when upload completes
74+
// used in css to hide progress spinner
75+
success: 'fb-upload-success',
76+
fail: 'fb-upload-fail'
77+
},
78+
79+
params: { 'csrf_token': '{{ csrf_token }}',
80+
'csrf_name': 'csrfmiddlewaretoken',
81+
'csrf_xname': 'X-CSRFToken',
82+
'temporary': 'true',
83+
'folder': '{{ final_attrs.temp_upload_dir }}', },
84+
85+
minSizeLimit: 0,
86+
debug: false,
87+
88+
getItem: function(id) {
89+
var items = $(this.element).find('.fb-upload-file').get();
90+
var item = items.pop();
91+
92+
while (typeof item != "undefined") {
93+
if (item.qqFileId == id) {
94+
return $(item);
95+
}
96+
item = items.pop();
97+
}
98+
},
99+
onProgress: function(id, fileName, loaded, total){
100+
var bar = $(this.element).find('.progress-bar .content');
101+
var new_width = '' + (loaded/total * 100) + '%';
102+
bar.css('width', new_width);
103+
},
104+
onComplete: function(id, fileName, resp){
105+
if (resp.success) {
106+
$(this.element).find('.progress-bar').fadeOut();
107+
$('#{{ final_attrs.id }}').val(resp.temp_filename);
108+
}
109+
},
110+
showMessage: function(message){ alert(message); }
111+
});
112+
});
113+
})(django.jQuery);
114+
</script>

0 commit comments

Comments
 (0)