I wasn't able to find in the web, even in this group, a multiple files upload system with the followings requirements:
- upload the files together with form submit - no frame, no flash, no ajax form submit but only jquery and python - give a title to the uploading files - simple to integrate and control in web2py so, inspired by http://the-stickman.com/web-development/javascript/upload-multiple-files-with-a-single-file-element/, I wrote one by my self. It's minimalist and the code isn't optimized (I'm not a programmer), but it works for my purposes. If you would like to test it in default welcome web2py app: - put jquery.multiuploads.js in static/js folder - put multiuploads.css in static/css folder - append the functions to default.py in controllers folder - append the table definition to db.py in models folder - put upload_documents.html in views folder You'll find some comments in the code and also many errors. I look forward to your suggestions and opinions. Ciao. Paolo{{response.files.extend([URL('static','css/multiuploads.css'),URL('static','js/jquery.multiuploads.js')])}} {{extend 'layout.html'}}
{{=form}}
jquery.multiuploads.js
Description: JavaScript source
/* CAUTION: essential rules don't remove */
div.multiupload_widget
{
display:inline-block; /* fix the div.error issue */
}
ul.mw_list
{
display:block;
}
input.mw_fileTitle
{
float:left;
}
div.mw_fileBrowserWrapper
{
float:left;
}
a.mw_addBtn
{
display:block;
position:relative;
overflow:hidden;
max-width:70px;
min-height:22px;
text-align:center;
margin-right:2px;
}
a.mw_clearBtn
{
float:left;
max-width:70px;
min-height:22px;
text-align:center;
margin-left:2px;
}
a.mw_delItem
{
display:block;
float:right;
}
ul.mw_list ul
{
width:90%; /* to avoid elastic when we slide it */
}
li.mw_group
{
cursor:pointer;
}
/* CUSTOMIZATION */
div.multiupload_widget
{
width:280px;
margin: 2px 15px 2px 5px;
padding:0 1px;
}
ul.mw_list
{
margin:0 0 4px 0;
padding:0;
min-height:32px;
border:1px solid #e1e1e1;
border-radius:2px;
overflow:auto;
list-style:none;
}
ul.mw_list>li
{
font-weight:bold;
display:block;
margin:2px 0;
}
ul.mw_list li:hover
{
background-color:lightyellow;
}
ul.mw_list ul
{
list-style:none;
margin-top:2px;
margin-left:22px;
width:256px; /*div.multiupload_widget - (margin-left + 2*(border width)) => 280-(22+2) */
}
ul.mw_list ul li
{
font-weight:normal;
}
ul.mw_list span
{
word-wrap:break-word;
display:inline-block;
width:80%;
margin:0 0 0 10px;
}
li.mw_group
{
}
li.mw_group span
{
}
div.mw_bar{}
input[type="text"].mw_fileTitle
{
width:69%;
padding-left:3px;
}
div.mw_fileBrowserWrapper{}
a.mw_addBtn, a.mw_clearBtn
{
width:24px;
height:22px;
background:#EAEAEA;
border:1px solid #DEDEDE;
border-radius:2px;
margin:0 2px 0 2px;
font-size:16px;
font-weight:bold;
line-height:22px;
}
a.mw_addBtn
{
line-height:24px;
}
a.mw_addBtn:hover, a.mw_clearBtn:hover
{
text-decoration:none;
}
a.mw_addBtn > span {}
a.mw_clearBtn > span {}
a.mw_delItem
{
width:16px;
height:16px;
background-color:silver;
color:white;
font-weight:bold;
border-radius:10px;
text-align:center;
font-size:13px;
line-height:13px;
text-decoration:none;
margin:2px 4px 0 0;
}
a.mw_delItem:hover
{
text-decoration:none;
color:#275B90;
background-color:#aaa;
}
/*----------------------------------------------------------------------------------*/
/* FILE ICON - based on http://nicolasgallagher.com/pure-css-gui-icons/
/*----------------------------------------------------------------------------------*/
ul.mw_list i
{
display: block;
float: left;
width: 12px;
height: 17px;
background: #eee;
margin-left: 4px;
margin-top: 2px;
position: relative;
}
ul.mw_list b
{
z-index:1;
overflow:hidden;
padding:0;
}
ul.mw_list b em
{
border-bottom: 1px solid #275B90;
color: #275B90;
display: block;
}
ul.mw_list b em::before, ul.mw_list b em::after
{
margin: -8px 0 0;
background: #275B90;
}
ul.mw_list b em::before
{
left: 5px;
width: 8px;
height: 12px;
border: 2px solid #275B90;
background: transparent;
}
ul.mw_list b em::after
{
left: 4px;
border-width: 3px;
border-style: solid;
border-color: white #275B90 #275B90 white;
margin-top: -9px;
background: transparent;
}
ul.mw_list b::before, ul.mw_list b::after, ul.mw_list b em::before, ul.mw_list b em::after
{
content:"";
position:absolute;
top:50%;
left:0;
}
/*----------------------------------------------------------------------------------*/def check(form):
# check if file list is empty. We, indeed, set notnull=False for f_document Field
for var in request.vars:
if var.startswith('f_document') and request.vars[var] != '': return
form.errors.f_document = T("file list is empty")
def upload_documents():
# only SQLFORM.factory tested
form=SQLFORM.factory(db.t_documents)
if form.accepts(request, session, onvalidation=lambda form:check(form)): # Is it possible to use onvalidation with form.process() syntax ?
nfiles = 0
for var in request.vars:
if var.startswith('f_document') and request.vars[var] != '':
uploaded = request.vars[var]
if isinstance(uploaded,list):
# files uploaded through input with "multiple" attribute set on true
counter=0
for element in uploaded:
counter += 1
nfiles += 1
file_title = element.name.split(":")[-1] # TODO: could this be made better?
# I mean, the title must be appended to element's name
# or is there another way?
db.t_documents.insert(
f_title=file_title+"("+str(counter)+")" if file_title!="" else file_title,
f_document=db.t_documents.f_document.store(element.file,element.filename))
else:
# only one file uploaded
element = request.vars[var]
nfiles += 1
db.t_documents.insert(
f_title=element.name.split(":")[-1],
f_document=db.t_documents.f_document.store(element.file,element.filename))
session.flash = T('%s document%s uploaded'%(nfiles, 's' if nfiles>1 else ''))
redirect(URL('documents_uploaded'))
if isinstance(form,FORM):
# hide f_title form's row. Is there a better way to accomplish it?
del form[0][0]
return dict(form=form)
def documents_uploaded():
headers = dict((str(k),(B(k.label))) for k in db.t_documents)
headers['t_documents.f_document']="Document file"
mytable = SQLFORM.grid(db.t_documents, headers=headers)
return dict(mytable = mytable)
def viewer():
redirect(URL('static','archive/documents',args=request.args(0)))db.define_table('t_documents',
Field('f_title', type='string', label=T('Title'), notnull=False), # notnull=False is required
Field('f_document', type='upload', uploadfolder=request.folder+'static/archive/documents', notnull=False, # notnull=False is required
label=T('Documents'),
represent=lambda x, row:x and A('%s'%(db.t_documents.f_document.retrieve(x)[0]),
_href=URL('default','viewer.html',args=x),
_target="_blank",
_title=T("open document"),
_class='file-reference')
or ''
),
format='%(f_title)s',
migrate=True
)<<attachment: result.png>>

