Merge pull request #482 from pdvyas/filemanager-hooks-new

hooked filemanager!
This commit is contained in:
Rushabh Mehta 2014-04-09 16:18:46 +05:30
commit d8c8f3fdfa
13 changed files with 435 additions and 151 deletions

View file

@ -1,72 +1,255 @@
{
"_last_update": null,
"_user_tags": null,
"allow_attach": null,
"allow_copy": null,
"allow_email": null,
"allow_import": null,
"allow_print": null,
"allow_rename": null,
"allow_trash": null,
"autoname": "File.######",
"creation": "2012-12-12 11:19:22.000000",
"change_log": null,
"client_script": null,
"client_script_core": null,
"client_string": null,
"colour": null,
"creation": "2012-12-12 11:19:22",
"custom": null,
"default_print_format": null,
"description": null,
"docstatus": 0,
"doctype": "DocType",
"document_type": null,
"dt_template": null,
"fields": [
{
"allow_on_submit": null,
"default": null,
"depends_on": null,
"description": null,
"fieldname": "file_name",
"fieldtype": "Data",
"hidden": null,
"ignore_restrictions": null,
"in_filter": null,
"in_list_view": 1,
"label": "File Name",
"no_column": null,
"no_copy": null,
"oldfieldname": "file_name",
"oldfieldtype": "Data",
"options": null,
"permlevel": 0,
"read_only": 1
"print_hide": null,
"print_width": null,
"read_only": 1,
"report_hide": null,
"reqd": null,
"search_index": null,
"set_only_once": null,
"trigger": null,
"width": null
},
{
"allow_on_submit": null,
"default": null,
"depends_on": null,
"description": null,
"fieldname": "file_url",
"fieldtype": "Data",
"hidden": null,
"ignore_restrictions": null,
"in_filter": null,
"in_list_view": 1,
"label": "File URL",
"no_column": null,
"no_copy": null,
"oldfieldname": null,
"oldfieldtype": null,
"options": null,
"permlevel": 0,
"read_only": 1
"print_hide": null,
"print_width": null,
"read_only": 1,
"report_hide": null,
"reqd": null,
"search_index": null,
"set_only_once": null,
"trigger": null,
"width": null
},
{
"allow_on_submit": null,
"default": null,
"depends_on": null,
"description": null,
"fieldname": "attached_to_doctype",
"fieldtype": "Link",
"hidden": null,
"ignore_restrictions": null,
"in_filter": null,
"in_list_view": 1,
"label": "Attached To DocType",
"no_column": null,
"no_copy": null,
"oldfieldname": null,
"oldfieldtype": null,
"options": "DocType",
"permlevel": 0,
"print_hide": null,
"print_width": null,
"read_only": 1,
"search_index": 1
"report_hide": null,
"reqd": null,
"search_index": 1,
"set_only_once": null,
"trigger": null,
"width": null
},
{
"allow_on_submit": null,
"default": null,
"depends_on": null,
"description": null,
"fieldname": "attached_to_name",
"fieldtype": "Data",
"hidden": null,
"ignore_restrictions": null,
"in_filter": null,
"in_list_view": 1,
"label": "Attached To Name",
"no_column": null,
"no_copy": null,
"oldfieldname": null,
"oldfieldtype": null,
"options": null,
"permlevel": 0,
"print_hide": null,
"print_width": null,
"read_only": 1,
"search_index": 1
"report_hide": null,
"reqd": null,
"search_index": 1,
"set_only_once": null,
"trigger": null,
"width": null
},
{
"allow_on_submit": null,
"default": null,
"depends_on": null,
"description": null,
"fieldname": "file_size",
"fieldtype": "Int",
"hidden": null,
"ignore_restrictions": null,
"in_filter": null,
"in_list_view": 1,
"label": "File Size",
"no_column": null,
"no_copy": null,
"oldfieldname": null,
"oldfieldtype": null,
"options": null,
"permlevel": 0,
"read_only": 1
"print_hide": null,
"print_width": null,
"read_only": 1,
"report_hide": null,
"reqd": null,
"search_index": null,
"set_only_once": null,
"trigger": null,
"width": null
},
{
"allow_on_submit": null,
"default": null,
"depends_on": null,
"description": null,
"fieldname": "content_hash",
"fieldtype": "Data",
"hidden": null,
"ignore_restrictions": null,
"in_filter": null,
"in_list_view": null,
"label": "Content Hash",
"no_column": null,
"no_copy": null,
"oldfieldname": null,
"oldfieldtype": null,
"options": null,
"permlevel": 0,
"print_hide": null,
"print_width": null,
"read_only": null,
"report_hide": null,
"reqd": null,
"search_index": 1,
"set_only_once": null,
"trigger": null,
"width": null
}
],
"hide_heading": null,
"hide_toolbar": null,
"icon": "icon-file",
"idx": 1,
"modified": "2014-01-20 17:48:46.000000",
"in_create": null,
"in_dialog": null,
"is_submittable": null,
"is_transaction_doc": null,
"issingle": null,
"istable": null,
"max_attachments": null,
"menu_index": null,
"modified": "2014-04-07 17:01:13.295614",
"modified_by": "Administrator",
"module": "Core",
"name": "File Data",
"name_case": null,
"owner": "Administrator",
"parent": null,
"parent_node": null,
"parentfield": null,
"parenttype": null,
"permissions": [
{
"cancel": 1,
"amend": null,
"cancel": 0,
"create": null,
"delete": 1,
"email": 1,
"export": null,
"import": null,
"match": null,
"permlevel": 0,
"print": 1,
"read": 1,
"report": null,
"restrict": null,
"restricted": null,
"role": "System Manager",
"submit": null,
"write": 1
}
],
"read_only": 0
"plugin": null,
"print_outline": null,
"read_only": 0,
"read_only_onload": null,
"search_fields": null,
"section_style": null,
"server_code": null,
"server_code_compiled": null,
"server_code_core": null,
"server_code_error": null,
"show_in_menu": null,
"smallicon": null,
"subject": null,
"tag_fields": null,
"title_field": null,
"use_template": null,
"version": null
}

View file

@ -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):
@ -19,10 +20,10 @@ class FileData(Document):
def on_update(self):
# check duplicate assignement
n_records = frappe.db.sql("""select name from `tabFile Data`
where file_name=%s
where content_hash=%s
and name!=%s
and attached_to_doctype=%s
and attached_to_name=%s""", (self.file_name, self.name, self.attached_to_doctype,
and attached_to_name=%s""", (self.content_hash, self.name, self.attached_to_doctype,
self.attached_to_name))
if len(n_records) > 0:
self.duplicate_entry = n_records[0][0]
@ -34,7 +35,7 @@ class FileData(Document):
if self.attached_to_name:
# check persmission
try:
if not self.ignore_permissions and \
if not getattr(self, 'ignore_permissions', False) and \
not frappe.has_permission(self.attached_to_doctype, "write", self.attached_to_name):
frappe.msgprint(frappe._("No permission to write / remove."), raise_exception=True)
@ -44,13 +45,8 @@ 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)
{"content_hash": self.content_hash, "name": ["!=", self.name]}):
delete_file_data_content(self)
def on_rollback(self):
self.on_trash()
self.on_trash()

View file

@ -48,3 +48,6 @@ doc_event:Website Route Permission:on_update = frappe.templates.generators.websi
doc_event:*:on_update = frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications
doc_event:*:on_cancel = frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications
doc_event:*:on_trash = frappe.core.doctype.notification_count.notification_count.clear_doctype_notifications
write_file_keys = file_url
write_file_keys = file_name

View file

@ -114,21 +114,14 @@ def update_child_docs(old, new, meta):
% (df.options, '%s', '%s'), (new, old))
def update_link_field_values(link_fields, old, new, doctype):
update_list = []
# update values
for field in link_fields:
# if already updated, do not do it again
if [field['parent'], field['fieldname']] in update_list:
continue
update_list.append([field['parent'], field['fieldname']])
if field['issingle']:
frappe.db.sql("""\
update `tabSingles` set value=%s
where doctype=%s and field=%s and value=%s""",
(new, field['parent'], field['fieldname'], old))
else:
if doctype!='DocType' and field['parent']!=new:
if field['parent']!=new:
frappe.db.sql("""\
update `tab%s` set `%s`=%s
where `%s`=%s""" \

View file

@ -16,11 +16,12 @@ frappe.patches.4_0.website_sitemap_hierarchy
frappe.patches.4_0.webnotes_to_frappe
execute:frappe.reset_perms("Module Def")
frappe.patches.4_0.rename_sitemap_to_route
frappe.patches.4_0.rename_profile_to_user
frappe.patches.4_0.set_website_route_idx
execute:import frappe.installer;frappe.installer.make_site_dirs() #2014-02-19
frappe.patches.4_0.private_backups
frappe.patches.4_0.set_module_in_report
frappe.patches.4_0.remove_old_parent
frappe.patches.4_0.rename_profile_to_user
frappe.patches.4_0.update_datetime
frappe.patches.4_0.deprecate_control_panel
frappe.patches.4_0.file_manager_hooks

View file

@ -0,0 +1,27 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import frappe
import os
from frappe.utils import get_files_path
from frappe.utils.file_manager import get_content_hash, get_file
def execute():
frappe.reload_doc('core', 'doctype', 'file_data')
for name, file_name, file_url in frappe.db.sql(
"""select name, file_name, file_url from `tabFile Data`
where file_name is not null"""):
b = frappe.get_doc('File Data', name)
old_file_name = b.file_name
b.file_name = os.path.basename(old_file_name)
if old_file_name.startswith('files/') or old_file_name.startswith('/files/'):
b.file_url = os.path.normpath('/' + old_file_name)
else:
b.file_url = os.path.normpath('/files/' + old_file_name)
_file_name, content = get_file(name)
b.content_hash = get_content_hash(content)
b.save()

View file

@ -1,6 +1,7 @@
import frappe
from frappe.model import rename_field
from frappe.model.meta import get_table_columns
def execute():
tables = frappe.db.sql_list("show tables")
@ -9,6 +10,7 @@ def execute():
if frappe.db.exists("DocType", "Website Route Permission"):
frappe.reload_doc("website", "doctype", "website_route_permission")
rename_field("Website Route Permission", "profile", "user")
if "profile" in get_table_columns("Website Route Permission"):
rename_field("Website Route Permission", "profile", "user")
frappe.reload_doc("website", "doctype", "blogger")
rename_field("Blogger", "profile", "user")

View file

@ -41,13 +41,14 @@ frappe.ui.form.Attachments = Class.extend({
this.$list.empty();
var attachments = this.get_attachments();
var file_names = keys(attachments).sort();
var that = this;
// add attachment objects
if(file_names.length) {
for(var i=0; i<file_names.length; i++) {
this.add_attachment(file_names[i], attachments);
}
if(attachments.length) {
attachments.forEach(function(attachment) {
that.add_attachment(attachment)
});
} else {
$('<p class="text-muted">' + frappe._("None") + '</p>').appendTo(this.$list);
}
@ -58,18 +59,23 @@ frappe.ui.form.Attachments = Class.extend({
get_attachments: function() {
return this.frm.get_docinfo().attachments;
},
add_attachment: function(filename, attachments) {
var fileid = attachments[filename];
add_attachment: function(attachment) {
var file_name = attachment.file_name;
var file_url = attachment.file_url;
var fileid = attachment.name;
if (!file_name) {
file_name = file_url;
}
var me = this;
var $attach = $(repl('<div class="alert alert-info" style="margin-bottom: 7px">\
<span style="display: inline-block; width: 90%; \
text-overflow: ellipsis; white-space: nowrap; overflow: hidden;">\
<i class="icon-file"></i> <a href="%(href)s"\
target="_blank" title="%(filename)s">%(filename)s</a></span><a href="#" class="close">&times;</a>\
<i class="icon-file"></i> <a href="%(file_url)s"\
target="_blank" title="%(file_name)s">%(file_name)s</a></span><a href="#" class="close">&times;</a>\
</div>', {
filename: filename,
href: frappe.utils.get_file_link(filename)
file_name: file_name,
file_url: file_url
}))
.appendTo(this.$list)
@ -83,7 +89,7 @@ frappe.ui.form.Attachments = Class.extend({
me.remove_attachment($(remove_btn).data("fileid"))
}
);
return false;
return false
});
if(!frappe.model.can_write(this.frm.doctype, this.frm.name)) {
@ -131,9 +137,9 @@ frappe.ui.form.Attachments = Class.extend({
doctype: this.frm.doctype,
docname: this.frm.docname,
},
callback: function(fileid, filename, r) {
callback: function(fileid, filename, file_url, r) {
me.dialog.hide();
me.update_attachment(fileid, filename, fieldname, r);
me.update_attachment(fileid, filename, file_url, fieldname, r.message);
},
onerror: function() {
me.dialog.hide();
@ -142,26 +148,27 @@ frappe.ui.form.Attachments = Class.extend({
max_height: this.frm.cscript ? this.frm.cscript.attachment_max_height : null,
});
},
update_attachment: function(fileid, filename, fieldname, r) {
update_attachment: function(fileid, filename, fieldname, file_url, attachment) {
if(fileid) {
this.add_to_attachments(fileid, filename);
this.add_to_attachments(attachment);
this.refresh();
if(fieldname) {
this.frm.set_value(fieldname, frappe.utils.get_file_link(filename));
this.frm.set_value(fieldname, file_url);
this.frm.cscript[fieldname] && this.frm.cscript[fieldname](this.frm.doc);
this.frm.toolbar.show_infobar();
}
}
},
add_to_attachments: function(fileid, filename) {
this.get_attachments()[filename] = fileid;
add_to_attachments: function (attachment) {
this.get_attachments().push(attachment);
},
remove_fileid: function(fileid) {
var attachments = this.get_attachments();
var new_attachments = {};
$.each(attachments, function(key, value) {
if(value!=fileid)
new_attachments[key] = value;
var new_attachments = [];
$.each(attachments, function(i, attachment) {
if(attachment.name!=fileid) {
new_attachments.push(attachment);
}
});
this.frm.get_docinfo().attachments = new_attachments;
this.refresh();
@ -181,4 +188,4 @@ frappe.ui.form.Attachments = Class.extend({
}
}
}
});
});

View file

@ -91,9 +91,9 @@ frappe.upload = {
opts.onerror ? opts.onerror(r) : opts.callback(null, null, r);
return;
}
opts.callback(r.message.fid, r.message.filename, r);
opts.callback(r.message.name , r.message.file_name, r.message.file_url, r);
$(document).trigger("upload_complete",
[r.message.fid, r.message.filename]);
[r.message.name , r.message.file_name]);
}
});
}
@ -123,4 +123,4 @@ frappe.upload = {
freader.readAsDataURL(fileobj);
}
}
}
}

View file

@ -0,0 +1,80 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
import frappe
import os
import unittest
from frappe.utils.file_manager import save_file, get_file, get_files_path
test_content1 = 'Hello'
test_content2 = 'Hello World'
def make_test_doc():
d = frappe.new_doc('ToDo')
d.description = 'Test'
d.save()
return d.doctype, d.name
class TestSimpleFile(unittest.TestCase):
def setUp(self):
self.attached_to_doctype, self.attached_to_docname = make_test_doc()
self.test_content = test_content1
self.saved_file = save_file('hello.txt', self.test_content, self.attached_to_doctype, self.attached_to_docname)
self.saved_filename = get_files_path(self.saved_file.file_name)
def test_save(self):
filename, content = get_file(self.saved_file.name)
self.assertEqual(content, self.test_content)
def tearDown(self):
# File gets deleted on rollback, so blank
pass
class TestSameFileName(unittest.TestCase):
def setUp(self):
self.attached_to_doctype, self.attached_to_docname = make_test_doc()
self.test_content1 = test_content1
self.test_content2 = test_content2
self.saved_file1 = save_file('hello.txt', self.test_content1, self.attached_to_doctype, self.attached_to_docname)
self.saved_file2 = save_file('hello.txt', self.test_content2, self.attached_to_doctype, self.attached_to_docname)
self.saved_filename1 = get_files_path(self.saved_file1.file_name)
self.saved_filename2 = get_files_path(self.saved_file2.file_name)
def test_saved_content(self):
filename1, content1 = get_file(self.saved_file1.name)
self.assertEqual(content1, self.test_content1)
filename2, content2 = get_file(self.saved_file2.name)
self.assertEqual(content2, self.test_content2)
def tearDown(self):
# File gets deleted on rollback, so blank
pass
class TestSameContent(unittest.TestCase):
def setUp(self):
self.attached_to_doctype1, self.attached_to_docname1 = make_test_doc()
self.attached_to_doctype2, self.attached_to_docname2 = make_test_doc()
self.test_content1 = test_content1
self.test_content2 = test_content1
self.orig_filename = 'hello.txt'
self.dup_filename = 'hello2.txt'
self.saved_file1 = save_file(self.orig_filename, self.test_content1, self.attached_to_doctype1, self.attached_to_docname1)
self.saved_file2 = save_file(self.dup_filename, self.test_content2, self.attached_to_doctype2, self.attached_to_docname2)
self.saved_filename1 = get_files_path(self.saved_file1.file_name)
self.saved_filename2 = get_files_path(self.saved_file2.file_name)
def test_saved_content(self):
filename1, content1 = get_file(self.saved_file1.name)
filename2, content2 = get_file(self.saved_file2.name)
self.assertEqual(filename1, filename2)
self.assertFalse(os.path.exists(get_files_path(self.dup_filename)))
def tearDown(self):
# File gets deleted on rollback, so blank
pass

View file

@ -782,8 +782,8 @@ def get_site_base_path(sites_dir=None, hostname=None):
def get_site_path(*path):
return get_path(base=get_site_base_path(), *path)
def get_files_path():
return get_site_path("public", "files")
def get_files_path(*path):
return get_site_path("public", "files", *path)
def get_backups_path():
return get_site_path("private", "backups")
@ -913,3 +913,11 @@ def touch_file(path):
def get_test_client():
from frappe.app import application
return Client(application)
def get_hook_method(hook_name, fallback=None):
method = (frappe.get_hooks().get(hook_name))
if method:
method = frappe.get_attr(method[0])
return method
if fallback:
return fallback

View file

@ -4,9 +4,12 @@
from __future__ import unicode_literals
import frappe
import os, base64, re
from frappe.utils import cstr, cint, get_site_path
import hashlib
import mimetypes
from frappe.utils import cstr, cint, get_site_path, get_hook_method, get_files_path
from frappe import _
from frappe import conf
from copy import copy
class MaxFileSizeReachedError(frappe.ValidationError): pass
@ -31,7 +34,11 @@ def upload():
elif file_url:
filedata = save_url(file_url, dt, dn)
return {"fid": filedata.name, "filename": filedata.file_name or filedata.file_url }
return {
"name": filedata.name,
"file_name": filedata.file_name,
"file_url": filedata.file_url
}
def save_uploaded(dt, dn):
fname, content = get_uploaded_content()
@ -76,11 +83,12 @@ def extract_images_from_html(doc, fieldname):
data = match.group(1)
headers, content = data.split(",")
filename = headers.split("filename=")[-1]
filename = save_file(filename, content, doc.doctype, doc.name, decode=True).get("file_name")
# TODO fix this
file_url = save_file(filename, content, doc.doctype, doc.name, decode=True).get("file_url")
if not frappe.flags.has_dataurl:
frappe.flags.has_dataurl = True
return '<img src="{filename}"'.format(filename = filename)
return '<img src="{file_url}"'.format(file_url=file_url)
if content:
content = re.sub('<img\s*src=\s*["\'](data:[^"\']*)["\']', _save_file, content)
@ -92,99 +100,49 @@ def save_file(fname, content, dt, dn, decode=False):
if isinstance(content, unicode):
content = content.encode("utf-8")
content = base64.b64decode(content)
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)
content_hash = get_content_hash(content)
content_type = mimetypes.guess_type(fname)[0]
fname = get_file_name(fname, content_hash[-6:])
fname_parts = fname.split(".", -1)
main = ".".join(fname_parts[:-1])
extn = fname_parts[-1]
versions = get_file_versions(files_path, main, extn)
if versions:
found_match = False
for version in versions:
if filecmp.cmp(os.path.join(files_path, version), temp_fname):
# remove new file, already exists!
os.remove(temp_fname)
fname = version
fpath = os.path.join(files_path, fname)
found_match = True
break
if not found_match:
# get_new_version name
fname = get_new_fname_based_on_version(files_path, main, extn, versions)
fpath = os.path.join(files_path, fname)
# rename
if os.path.exists(fpath.encode("utf-8")):
frappe.throw("File already exists: " + fname)
os.rename(temp_fname, fpath.encode("utf-8"))
else:
fpath = os.path.join(files_path, fname)
# rename new file
if os.path.exists(fpath.encode("utf-8")):
frappe.throw("File already exists: " + fname)
os.rename(temp_fname, fpath.encode("utf-8"))
method = get_hook_method('write_file', fallback=save_file_on_filesystem)
f = frappe.get_doc({
file_data = get_file_data_from_hash(content_hash)
if not file_data:
file_data = method(fname, content, content_type=content_type)
file_data = copy(file_data)
file_data.update({
"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
"file_size": file_size,
"content_hash": content_hash,
})
f = frappe.get_doc(file_data)
f.ignore_permissions = True
try:
f.insert();
except frappe.DuplicateEntryError:
return frappe.get_doc("File Data", f.duplicate_entry)
return f
def get_file_versions(files_path, main, extn):
out = []
for f in os.listdir(files_path):
f = cstr(f)
if f.startswith(main) and f.endswith(extn):
out.append(f)
return out
def get_new_fname_based_on_version(files_path, main, extn, versions):
versions.sort()
if "-" in versions[-1]:
version = cint(versions[-1].split("-")[-1]) or 1
else:
version = 1
def get_file_data_from_hash(content_hash):
for name in frappe.db.sql_list("select name from `tabFile Data` where content_hash='{}'".format(content_hash)):
b = frappe.get_doc('File Data', name)
return {k:b.get(k) for k in frappe.get_hooks()['write_file_keys']}
return False
new_fname = main + "-" + str(version) + "." + extn
while os.path.exists(os.path.join(files_path, new_fname).encode("utf-8")):
version += 1
new_fname = main + "-" + str(version) + "." + extn
if version > 100:
frappe.msgprint("Too many versions", raise_exception=True)
return new_fname
def scrub_file_name(fname):
if '\\' in fname:
fname = fname.split('\\')[-1]
if '/' in fname:
fname = fname.split('/')[-1]
return fname
def save_file_on_filesystem(fname, content, content_type=None):
import filecmp
public_path = os.path.join(frappe.local.site_path, "public")
fpath = write_file(content, get_files_path(), fname)
path = os.path.relpath(fpath, public_path)
return {
'file_name': os.path.basename(path),
'file_url': '/' + path
}
def check_max_file_size(content):
max_file_size = conf.get('max_file_size') or 1000000
@ -196,17 +154,14 @@ def check_max_file_size(content):
return file_size
def write_file(content, files_path):
def write_file(content, file_path, fname):
"""write file to disk with a random name (to compare)"""
# create account folder (if not exists)
frappe.create_folder(files_path)
fname = os.path.join(files_path, frappe.generate_hash())
# create directory (if not exists)
frappe.create_folder(get_files_path())
# write the file
with open(fname, 'w+') as f:
with open(os.path.join(file_path, fname), 'w+') as f:
f.write(content)
return fname
return get_files_path(fname)
def remove_all(dt, dn):
"""remove all files in a transaction"""
@ -220,6 +175,19 @@ 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(doc):
method = get_hook_method('delete_file_data_content', fallback=delete_file_from_filesystem)
method(doc)
def delete_file_from_filesystem(doc):
path = doc.file_name
if path.startswith("files/"):
path = frappe.utils.get_site_path("public", doc.file_name)
else:
path = frappe.utils.get_site_path("public", "files", 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 +205,14 @@ def get_file(fname):
content = f.read()
return [file_name, content]
def get_content_hash(content):
return hashlib.md5(content).hexdigest()
def get_file_name(fname, optional_suffix):
n_records = frappe.db.sql("select name from `tabFile Data` where file_name='{}'".format(fname))
if len(n_records) > 0:
partial, extn = fname.rsplit('.', 1)
return '{partial}{suffix}.{extn}'.format(partial=partial, extn=extn, suffix=optional_suffix)
return fname

View file

@ -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):
@ -88,11 +89,15 @@ def get_restrictions(meta):
return out
def add_attachments(dt, dn):
attachments = {}
attachments = []
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.append({
'name': f.name,
'file_url': f.file_url,
'file_name': f.file_name
})
return attachments
@ -122,4 +127,4 @@ def get_badge_info(doctypes, filters):
for doctype in doctypes:
out[doctype] = frappe.db.get_value(doctype, filters, "count(*)")
return out
return out