diff --git a/frappe/core/doctype/file_data/file_data.py b/frappe/core/doctype/file_data/file_data.py index 7b58f9e905..c6f9d00005 100644 --- a/frappe/core/doctype/file_data/file_data.py +++ b/frappe/core/doctype/file_data/file_data.py @@ -11,6 +11,7 @@ naming for same name files: file.gif, file-1.gif, file-2.gif etc import frappe, frappe.utils, os from frappe import conf from frappe.model.document import Document +from frappe.utils.file_manager import delete_file_data_content class FileData(Document): def before_insert(self): @@ -45,12 +46,7 @@ class FileData(Document): # if file not attached to any other record, delete it if self.file_name and not frappe.db.count("File Data", {"file_name": self.file_name, "name": ["!=", self.name]}): - if self.file_name.startswith("files/"): - path = frappe.utils.get_site_path("public", self.file_name) - else: - path = frappe.utils.get_site_path("public", "files", self.file_name) - if os.path.exists(path): - os.remove(path) + delete_file_data_content(self.file_name) def on_rollback(self): - self.on_trash() \ No newline at end of file + self.on_trash() diff --git a/frappe/utils/file_manager.py b/frappe/utils/file_manager.py index 767994b712..036d6a01f1 100644 --- a/frappe/utils/file_manager.py +++ b/frappe/utils/file_manager.py @@ -4,6 +4,8 @@ from __future__ import unicode_literals import frappe import os, base64, re +import hashlib +import mimetypes from frappe.utils import cstr, cint, get_site_path from frappe import _ from frappe import conf @@ -92,16 +94,40 @@ def save_file(fname, content, dt, dn, decode=False): if isinstance(content, unicode): content = content.encode("utf-8") content = base64.b64decode(content) + + file_size = check_max_file_size(content) + content_hash = get_content_hash(content) + content_type = mimetypes.guess_type(fname)[0] + + method = (webnotes.get_hooks().get('write_file')) + if method: + method = webnotes.get_attr(method[0]) + else: + method = save_file_on_filesystem + file_path = method(fname, content, content_hash, content_type=content_type) + + f = webnotes.bean({ + "doctype": "File Data", + "file_name": file_path, + "attached_to_doctype": dt, + "attached_to_name": dn, + "file_size": file_size, + "file_hash": content_hash + }) + f.ignore_permissions = True + try: + f.insert(); + except webnotes.DuplicateEntryError: + return webnotes.doc("File Data", f.doc.duplicate_entry) + + return f.doc +def save_file_on_filesystem(fname, content, content_hash, content_type=None): import filecmp from frappe.modules import load_doctype_module files_path = os.path.join(frappe.local.site_path, "public", "files") module = load_doctype_module(dt, frappe.db.get_value("DocType", dt, "module")) - if hasattr(module, "attachments_folder"): - files_path = os.path.join(files_path, module.attachments_folder) - - file_size = check_max_file_size(content) temp_fname = write_file(content, files_path) fname = scrub_file_name(fname) @@ -139,21 +165,8 @@ def save_file(fname, content, dt, dn, decode=False): frappe.throw("File already exists: " + fname) os.rename(temp_fname, fpath.encode("utf-8")) - - f = frappe.get_doc({ - "doctype": "File Data", - "file_name": os.path.relpath(os.path.join(files_path, fname), get_site_path("public")), - "attached_to_doctype": dt, - "attached_to_name": dn, - "file_size": file_size - }) - f.ignore_permissions = True - try: - f.insert(); - except frappe.DuplicateEntryError: - return frappe.get_doc("File Data", f.duplicate_entry) - - return f + return os.path.relpath(fpath, + get_site_path(conf.get("public_path", "public"))) def get_file_versions(files_path, main, extn): out = [] @@ -220,6 +233,22 @@ def remove_all(dt, dn): def remove_file(fid): """Remove file and File Data entry""" frappe.delete_doc("File Data", fid) + +def delete_file_data_content(path): + method = (frappe.get_hooks().get('delete_file_data_content')) + if method: + method = frappe.get_attr(method[0]) + else: + method = delete_file_from_filesystem + method(path) + +def delete_file_from_filesystem(path): + if path.startswith("files/"): + path = frappe.utils.get_site_path("public", self.doc.file_name) + else: + path = frappe.utils.get_site_path("public", "files", self.doc.file_name) + if os.path.exists(path): + os.remove(path) def get_file(fname): f = frappe.db.sql("""select file_name from `tabFile Data` @@ -237,3 +266,18 @@ def get_file(fname): content = f.read() return [file_name, content] + +def get_content_hash(content): + return hashlib.md5(content).hexdigest() + +def get_file_url(file_data_doc): + if file_data_doc.file_url: + return file_url + + method = (webnotes.get_hooks().get('get_file_data_url')) + if method: + method = webnotes.get_attr(method[0]) + return method(file_data_doc.file_name) + else: + return file_name + diff --git a/frappe/widgets/form/load.py b/frappe/widgets/form/load.py index 67e23369a2..28e84e2f66 100644 --- a/frappe/widgets/form/load.py +++ b/frappe/widgets/form/load.py @@ -6,6 +6,7 @@ import frappe, json import frappe.utils import frappe.defaults import frappe.widgets.form.meta +from frappe.utils.file_manager import get_file_url @frappe.whitelist() def getdoc(doctype, name, user=None): @@ -92,7 +93,7 @@ def add_attachments(dt, dn): for f in frappe.db.sql("""select name, file_name, file_url from `tabFile Data` where attached_to_name=%s and attached_to_doctype=%s""", (dn, dt), as_dict=True): - attachments[f.file_url or f.file_name] = f.name + attachments[get_file_url(f)] = f.name return attachments @@ -122,4 +123,4 @@ def get_badge_info(doctypes, filters): for doctype in doctypes: out[doctype] = frappe.db.get_value(doctype, filters, "count(*)") - return out \ No newline at end of file + return out