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}}

Attachment: 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>>

Reply via email to