diff --git a/core/doctype/file_data/file_data.txt b/core/doctype/file_data/file_data.txt index 728cdf1c92..37ea5f6e9d 100644 --- a/core/doctype/file_data/file_data.txt +++ b/core/doctype/file_data/file_data.txt @@ -2,19 +2,16 @@ { "owner": "Administrator", "docstatus": 0, - "creation": "2012-03-27 14:35:37", + "creation": "2012-11-30 18:13:34", "modified_by": "Administrator", - "modified": "2012-03-27 14:35:37" + "modified": "2012-12-11 14:56:34" }, { "read_only": 0, - "section_style": "Simple", + "autoname": "FileData/.#####", "name": "__common__", - "colour": "White:FFF", - "module": "Core", - "server_code_error": " ", "doctype": "DocType", - "autoname": "FileData/.#####" + "module": "Core" }, { "name": "__common__", @@ -36,6 +33,12 @@ "fieldname": "file_name", "fieldtype": "Data" }, + { + "doctype": "DocField", + "label": "File URL", + "fieldname": "file_url", + "fieldtype": "Data" + }, { "oldfieldtype": "Link", "doctype": "DocField", @@ -44,13 +47,5 @@ "fieldname": "module", "fieldtype": "Link", "options": "Module Def" - }, - { - "oldfieldtype": "Blob", - "doctype": "DocField", - "label": "Blob Content", - "oldfieldname": "blob_content", - "fieldname": "blob_content", - "fieldtype": "Blob" } ] \ No newline at end of file diff --git a/core/doctype/profile/profile.js b/core/doctype/profile/profile.js index 163bc2f064..59bacdb85f 100644 --- a/core/doctype/profile/profile.js +++ b/core/doctype/profile/profile.js @@ -26,7 +26,7 @@ cur_frm.cscript.refresh = function(doc) { wn.ui.set_user_background(doc.background_image); } if(doc.user_image) { - wn.boot.user_info[user].image = 'files/' + doc.user_image; + wn.boot.user_info[user].image = wn.utils.get_file_link(doc.user_image); } } } diff --git a/public/build.json b/public/build.json index bd343f984d..2678025f3c 100644 --- a/public/build.json +++ b/public/build.json @@ -143,8 +143,8 @@ "lib/public/js/legacy/widgets/form/form_comments.js", "lib/public/js/legacy/wn/widgets/form/sidebar.js", "lib/public/js/legacy/wn/widgets/form/comments.js", - "lib/public/js/legacy/wn/widgets/form/attachments.js", "lib/public/js/legacy/wn/widgets/form/assign_to.js", + "lib/public/js/wn/form/attachments.js", "lib/public/js/wn/form/linked_with.js", "lib/public/js/wn/print/print_table.js", diff --git a/public/css/ui/views.css b/public/css/ui/views.css index e6d4d84592..50153ee53f 100644 --- a/public/css/ui/views.css +++ b/public/css/ui/views.css @@ -1,3 +1,7 @@ +h5 { + margin-bottom: 0px; +} + .layout_wrapper, .layout-wrapper { -moz-box-shadow: 0px 0px 3px rgba(0,0,0,0.9); -webkit-box-shadow: 0px 0px 3px rgba(0,0,0,0.9); diff --git a/public/js/legacy/widgets/form/fields.js b/public/js/legacy/widgets/form/fields.js index c9e43efa48..74fa654c0b 100644 --- a/public/js/legacy/widgets/form/fields.js +++ b/public/js/legacy/widgets/form/fields.js @@ -533,10 +533,11 @@ DateField.prototype.validate = function(v) { me.input.set_input(''); return ''; } - var t = v.split('-'); + var t = $.map(v.split('-'), function(part) { return cint(part) ? part : null; }); if(t.length!=3) { return this.clear(); } else if(cint(t[1])>12 || cint(t[1])<1) { return this.clear(); } else if(cint(t[2])>31 || cint(t[2])<1) { return this.clear(); } + else if(String(cint(t[0])).length!=4) { return this.clear(); } // 4 char for year return v; }; @@ -1101,7 +1102,7 @@ SelectField.prototype.make_input = function() { this.df.options = ''; var fl = fl.split('\n'); for(var i in fl) { - this.df.options += '\n' + fl[i].split(',')[1]; + this.df.options += '\n' + fl[i].split(',')[0]; this.set_description(""); } } else { diff --git a/public/js/legacy/widgets/form/form_fields.js b/public/js/legacy/widgets/form/form_fields.js index fe336e3cc9..85e7868819 100644 --- a/public/js/legacy/widgets/form/form_fields.js +++ b/public/js/legacy/widgets/form/form_fields.js @@ -165,7 +165,7 @@ _f.ImageField.prototype.onrefresh = function() { $(this.label_span).toggle(false); $(this.wrapper).find("img").remove(); if(this.df.options && this.frm.doc[this.df.options]) { - $("") + $("") .appendTo(this.wrapper); } } diff --git a/public/js/legacy/wn/widgets/form/attachments.js b/public/js/legacy/wn/widgets/form/attachments.js deleted file mode 100644 index dc6a948116..0000000000 --- a/public/js/legacy/wn/widgets/form/attachments.js +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com) -// -// MIT License (MIT) -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -// CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -// OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -wn.widgets.form.sidebar.Attachments = function(parent, sidebar, doctype, docname) { - var me = this; - this.frm = sidebar.form; - - this.make = function() { - if(this.wrapper) this.wrapper.innerHTML = ''; - else this.wrapper = $a(parent, 'div', 'sidebar-comment-wrapper'); - - // attachment - this.attach_wrapper = $a(this.wrapper, 'div'); - - // no attachments if file is unsaved - if(this.frm.doc.__islocal) { - this.attach_wrapper.innerHTML = '
Attachments can be \ - uploaded after saving
'; - return; - } - - // no of attachments - var n = this.frm.doc.file_list ? this.frm.doc.file_list.split('\n').length : 0; - - // button if the number of attachments is less than max - if(n < this.frm.meta.max_attachments || !this.frm.meta.max_attachments) { - this.btn = $btn($a(this.wrapper, 'div'), 'Add new attachment', - function() { me.add_attachment() }); - } - - // render - this.render(); - - } - - // create Attachment objects from - // the file_list - this.render = function() { - // clear exisitng - this.attach_wrapper.innerHTML = '' - - var doc = locals[me.frm.doctype][me.frm.docname]; - var fl = doc.file_list ? doc.file_list.split('\n') : []; - - // add attachment objects - for(var i=0; i\ +
\ + \ + ').appendTo(this.parent); + this.$list = this.wrapper.find(".alert-list"); + + this.parent.find(".btn").click(function() { + me.new_attachment(); + }) + }, + max_reached: function() { + // no of attachments + var n = this.frm.doc.file_list ? this.frm.doc.file_list.split('\n').length : 0; + + // button if the number of attachments is less than max + if(n < this.frm.meta.max_attachments || !this.frm.meta.max_attachments) { + return false; + } + return true; + }, + refresh: function() { + if(this.frm.doc.__islocal || !this.frm.meta.allow_attach) { + this.parent.toggle(false); + return; + } + this.parent.toggle(true); + this.parent.find(".btn").toggle(!this.max_reached()) + + this.$list.empty(); + + var fl = this.get_filelist(); + + // add attachment objects + for(var i=0; i\ + %(filename)s×\ + ', { + filename: filename, + href: wn.utils.get_file_link(filename) + })) + .appendTo(this.$list) + .find(".close") + .data("fileid", fileid) + .click(function() { + var yn = confirm("Are you sure you want to delete the attachment?"); + if(!yn) return; + + var data = $(this).data("fileid"); + wn.call({ + method: 'webnotes.widgets.form.utils.remove_attach', + args: { + 'fid': data, + dt: me.frm.doctype, + dn: me.frm.docname + }, + callback: function(r,rt) { + me.frm.doc.modified = r.message; + me.remove_fileid(data); + me.frm.refresh(); + } + }); + return false; + }); + }, + new_attachment: function() { + if(!this.dialog) { + this.dialog = new wn.ui.Dialog({ + title:'Upload Attachment', + width: 400 + }) + $y(this.dialog.body, {margin:'13px'}) + this.dialog.make(); + } + this.dialog.body.innerHTML = ''; + this.dialog.show(); + + wn.upload.make({ + parent: this.dialog.body, + args: { + from_form: 1, + doctype: this.frm.doctype, + docname: this.frm.docname + }, + callback: wn.ui.form.file_upload_done + }); + + }, + remove_fileid: function(fileid) { + this.frm.doc.file_list = $.map(this.get_filelist(), function(f) { + if(f.split(',')[1]!=fileid) return f; + }).join('\n'); + } +}) + + +// this function will be called after the upload is done +// from webnotes.utils.file_manager +wn.ui.form.file_upload_done = function(doctype, docname, fileid, filename, at_id, new_timestamp) { + + // add to file_list + var doc = locals[doctype][docname]; + if(doc.file_list) { + var fl = doc.file_list.split('\n') + fl.push(filename + ',' + fileid) + doc.file_list = fl.join('\n'); + } + else + doc.file_list = filename + ',' + fileid; + + // update timestamp + doc.modified = new_timestamp; + + // update file_list + var frm = wn.views.formview[doctype].frm; + frm.attachments.dialog.hide(); + msgprint('File Uploaded Sucessfully.'); + frm.refresh(); +} diff --git a/public/js/wn/misc/user.js b/public/js/wn/misc/user.js index 79cc58e2f4..6848c32d72 100644 --- a/public/js/wn/misc/user.js +++ b/public/js/wn/misc/user.js @@ -15,7 +15,7 @@ wn.user_info = function(uid) { } wn.avatar = function(user, large, title) { - var image = wn.user_info(user).image; + var image = wn.utils.get_file_link(wn.user_info(user).image); var to_size = large ? 72 : 30; if(!title) title = wn.user_info(user).fullname; diff --git a/public/js/wn/misc/utils.js b/public/js/wn/misc/utils.js index 43a22c501f..dc49440ce9 100644 --- a/public/js/wn/misc/utils.js +++ b/public/js/wn/misc/utils.js @@ -1,6 +1,14 @@ wn.provide('wn.utils'); wn.utils = { + get_file_link: function(filename) { + return wn.utils.is_url(filename) || (filename.indexOf("images/")!=-1) + ? filename : 'files/' + filename; + }, + is_url: function(txt) { + return txt.toLowerCase().substr(0,7)=='http://' + || txt.toLowerCase().substr(0,8)=='https://' + }, filter_dict: function(dict, filters) { var ret = []; if(typeof filters=='string') { diff --git a/public/js/wn/upload.js b/public/js/wn/upload.js index 6b9c2743cd..561bf7c124 100644 --- a/public/js/wn/upload.js +++ b/public/js/wn/upload.js @@ -6,8 +6,11 @@ wn.upload = { style="width:0px; height:0px; border:0px">\
\ + Upload A File:
\

\ - \ + OR:

\ +

e.g. http://example.com/somefile.png


\ + \
', { id: id, action: wn.request.url diff --git a/public/js/wn/views/listview.js b/public/js/wn/views/listview.js index 4132d315cf..2be5c6aa1c 100644 --- a/public/js/wn/views/listview.js +++ b/public/js/wn/views/listview.js @@ -42,15 +42,12 @@ wn.views.ListView = Class.extend({ $(parent).append(repl('%(name)s', data)); } else if(opts.content=='avatar') { - $(parent).append(repl('', - data)); + $(parent).append(wn.avatar(data.owner, false, "Created by: " + + wn.user_info(data.owner).fullname)); } else if(opts.content=='avatar_modified') { - $(parent).append(repl('\ - ', - data)); + $(parent).append(wn.avatar(data.modified_by, false, "Modified by: " + + wn.user_info(data.modified_by).fullname)); } else if(opts.content=='check') { $(parent).append(''); @@ -106,12 +103,6 @@ wn.views.ListView = Class.extend({ }); }, prepare_data: function(data) { - data.fullname = wn.user_info(data.owner).fullname; - data.avatar = wn.user_info(data.owner).image; - - data.fullname_modified = wn.user_info(data.modified_by).fullname; - data.avatar_modified = wn.user_info(data.modified_by).image; - if(data.modified) this.prepare_when(data, data.modified); diff --git a/webnotes/boot.py b/webnotes/boot.py index ecbd857355..2504330101 100644 --- a/webnotes/boot.py +++ b/webnotes/boot.py @@ -87,7 +87,7 @@ def get_fullnames(): if not r[2]: r[2] = 'lib/images/ui/avatar.png' else: - r[2] = 'files/' + r[2] + r[2] = r[2] d[r[0]]= {'fullname': r[1], 'image': r[2], 'gender': r[3], 'email': r[4] or r[0]} diff --git a/webnotes/utils/__init__.py b/webnotes/utils/__init__.py index 941c76c045..0794cdfc58 100644 --- a/webnotes/utils/__init__.py +++ b/webnotes/utils/__init__.py @@ -153,10 +153,11 @@ def getdate(string_date): if " " in string_date: string_date = string_date.split(" ")[0] - try: + try: return datetime.datetime.strptime(string_date, "%Y-%m-%d").date() except ValueError, e: - return "" + webnotes.msgprint("Cannot understand date - '%s'" % \ + (string_date,), raise_exception=1) def add_to_date(date, years=0, months=0, days=0): """Adds `days` to the given date""" diff --git a/webnotes/utils/file_manager.py b/webnotes/utils/file_manager.py index 6fb82423dd..83ad554fa4 100644 --- a/webnotes/utils/file_manager.py +++ b/webnotes/utils/file_manager.py @@ -26,23 +26,27 @@ import os, conf def upload(): # get record details - dt = webnotes.form_dict.get('doctype') - dn = webnotes.form_dict.get('docname') - at_id = webnotes.form_dict.get('at_id') - - webnotes.response['type'] = 'iframe' + dt = webnotes.form_dict.doctype + dn = webnotes.form_dict.docname + at_id = webnotes.form_dict.at_id + file_url = webnotes.form_dict.file_url filename = webnotes.form['filedata'].filename - if not filename: + + webnotes.response['type'] = 'iframe' + if not filename and not file_url: webnotes.response['result'] = """ """ % dt return # save - fid, fname = save_uploaded() - + if filename: + fid, fname = save_uploaded() + elif file_url: + fid, fname = save_url(file_url) + # save it in the form updated = False if fid: @@ -53,7 +57,7 @@ def upload(): # with the new modified timestamp webnotes.response['result'] = """ """ % { @@ -65,22 +69,85 @@ window.parent.wn.views.formview['%(dt)s'].frm.show_doc('%(dn)s'); 'mod': webnotes.conn.get_value(dt, dn, 'modified') } -# ------------------------------------------------------- +def save_uploaded(): + webnotes.response['type'] = 'iframe' + fname, content = get_uploaded_content() + if content: + fid = save_file(fname, content) + return fid, fname + else: + return None, fname + +def save_url(file_url): + f = webnotes.doc("File Data") + f.file_url = file_url + f.file_name = file_url.split('/')[-1] + f.save(new=1) + return f.name, file_url + +def get_uploaded_content(): + # should not be unicode when reading a file, hence using webnotes.form + if 'filedata' in webnotes.form: + i = webnotes.form['filedata'] + webnotes.uploaded_filename, webnotes.uploaded_content = i.filename, i.file.read() + return webnotes.uploaded_filename, webnotes.uploaded_content + else: + webnotes.msgprint('No File'); + return None, None + +def save_file(fname, content, module=None): + from webnotes.model.doc import Document + from filecmp import cmp + + check_max_file_size(content) + new_fname = write_file(content) + + # some browsers return the full path + if '\\' in fname: + fname = fname.split('\\')[-1] + if '/' in fname: + fname = fname.split('/')[-1] + + # we use - for versions, so remove them from the name! + fname = fname.replace('-', '') + + fpath = os.path.join(get_files_path(), fname) + if os.path.exists(fpath) and cmp(fpath, new_fname): + # remove new file, already exists! + os.remove(new_fname) + return fname + else: + # generate the ID (?) + f = Document('File Data') + f.file_name = fname + f.save(1) + # rename new file + os.rename(new_fname, os.path.join(get_files_path(), f.name)) + return f.name + +def check_max_file_size(content): + max_file_size = getattr(conf, 'max_file_size', 1000000) + + if len(content) > max_file_size: + raise Exception, 'Maximum File Limit (%s MB) Crossed' % (int(max_file_size / 1000000)) + +def write_file(content): + """write file to disk with a random name (to compare)""" + # create account folder (if not exists) + webnotes.create_folder(get_files_path()) + fname = os.path.join(get_files_path(), webnotes.generate_hash()) + + # write the file + with open(fname, 'w+') as f: + f.write(content) + + return fname def add_file_list(dt, dn, fname, fid): - """ - udpate file_list attribute of the record - """ fl = webnotes.conn.get_value(dt, dn, 'file_list') or '' - if fl: - fl += '\n' - - # add new file id + if fl: fl += '\n' fl += fname + ',' + fid - - # save webnotes.conn.set_value(dt, dn, 'file_list', fl) - return True def remove_all(dt, dn): @@ -111,87 +178,6 @@ def remove_file(dt, dn, fid): # return the new timestamp return webnotes.conn.get_value(dt, dn, 'modified') -def make_thumbnail(blob, size): - from PIL import Image - from cStringIO import StringIO - - fobj = StringIO(blob) - image = Image.open(fobj) - image.thumbnail((tn,tn*2), Image.ANTIALIAS) - outfile = cStringIO.StringIO() - image.save(outfile, 'JPEG') - outfile.seek(0) - fcontent = outfile.read() - - return fcontent - -def get_uploaded_content(): - # should not be unicode when reading a file, hence using webnotes.form - if 'filedata' in webnotes.form: - i = webnotes.form['filedata'] - webnotes.uploaded_filename, webnotes.uploaded_content = i.filename, i.file.read() - return webnotes.uploaded_filename, webnotes.uploaded_content - else: - webnotes.msgprint('No File'); - return None, None - -def save_uploaded(): - webnotes.response['type'] = 'iframe' - fname, content = get_uploaded_content() - if content: - fid = save_file(fname, content) - return fid, fname - else: - return None, fname - -def save_file(fname, content, module=None): - from webnotes.model.doc import Document - from filecmp import cmp - - check_max_file_size(content) - new_fname = write_file(content) - - # some browsers return the full path - if '\\' in fname: - fname = fname.split('\\')[-1] - if '/' in fname: - fname = fname.split('/')[-1] - - # we use - for versions, so remove them from the name! - fname = fname.replace('-', '') - - fpath = os.path.join(get_files_path(), fname) - if os.path.exists(fpath) and cmp(fpath, new_fname): - # remove file, already exists! - os.remove(new_fname) - return fname - else: - # generate the ID (?) - f = Document('File Data') - f.file_name = fname - f.save(1) - # rename new file - os.rename(new_fname, os.path.join(get_files_path(), f.name)) - return f.name - -def check_max_file_size(content): - max_file_size = getattr(conf, 'max_file_size', 1000000) - - if len(content) > max_file_size: - raise Exception, 'Maximum File Limit (%s MB) Crossed' % (int(max_file_size / 1000000)) - -def write_file(content): - """write file to disk with a random name""" - # create account folder (if not exists) - webnotes.create_folder(get_files_path()) - fname = os.path.join(get_files_path(), webnotes.generate_hash()) - - # write the file - with open(fname, 'w+') as f: - f.write(content) - - return fname - def get_file_system_name(fname): # get system name from File Data table return webnotes.conn.sql("""select name, file_name from `tabFile Data` @@ -228,4 +214,18 @@ def get_files_path(): import os, conf files_path = os.path.join(os.path.dirname(os.path.abspath(conf.__file__)), 'public', 'files') - return files_path \ No newline at end of file + return files_path + +def make_thumbnail(blob, size): + from PIL import Image + from cStringIO import StringIO + + fobj = StringIO(blob) + image = Image.open(fobj) + image.thumbnail((tn,tn*2), Image.ANTIALIAS) + outfile = cStringIO.StringIO() + image.save(outfile, 'JPEG') + outfile.seek(0) + fcontent = outfile.read() + + return fcontent \ No newline at end of file