[enhancement] added 'make_thumbnail' to File and will be saved as thumbnail_urlversion-14
@@ -469,11 +469,14 @@ def get_precision(doctype, fieldname, currency=None, doc=None): | |||
from frappe.model.meta import get_field_precision | |||
return get_field_precision(get_meta(doctype).get_field(fieldname), doc, currency) | |||
def generate_hash(txt=None): | |||
def generate_hash(txt=None, length=None): | |||
"""Generates random hash for given text + current timestamp + random string.""" | |||
import hashlib, time | |||
from .utils import random_string | |||
return hashlib.sha224((txt or "") + repr(time.time()) + repr(random_string(8))).hexdigest() | |||
digest = hashlib.sha224((txt or "") + repr(time.time()) + repr(random_string(8))).hexdigest() | |||
if length: | |||
digest = digest[:length] | |||
return digest | |||
def reset_metadata_version(): | |||
"""Reset `metadata_version` (Client (Javascript) build ID) hash.""" | |||
@@ -2,7 +2,7 @@ | |||
"allow_copy": 0, | |||
"allow_import": 1, | |||
"allow_rename": 0, | |||
"autoname": "hash", | |||
"autoname": "", | |||
"creation": "2012-12-12 11:19:22", | |||
"custom": 0, | |||
"docstatus": 0, | |||
@@ -205,6 +205,28 @@ | |||
"set_only_once": 0, | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
"collapsible": 0, | |||
"fieldname": "thumbnail_url", | |||
"fieldtype": "Data", | |||
"hidden": 0, | |||
"ignore_user_permissions": 0, | |||
"in_filter": 0, | |||
"in_list_view": 0, | |||
"label": "Thumbnail URL", | |||
"no_copy": 0, | |||
"permlevel": 0, | |||
"precision": "", | |||
"print_hide": 0, | |||
"read_only": 1, | |||
"report_hide": 0, | |||
"reqd": 0, | |||
"search_index": 0, | |||
"set_only_once": 0, | |||
"unique": 0 | |||
}, | |||
{ | |||
"allow_on_submit": 0, | |||
"bold": 0, | |||
@@ -433,7 +455,7 @@ | |||
"is_submittable": 0, | |||
"issingle": 0, | |||
"istable": 0, | |||
"modified": "2015-09-18 06:22:10.902847", | |||
"modified": "2015-10-07 05:52:52.922698", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "File", | |||
@@ -40,7 +40,7 @@ class File(NestedSet): | |||
# home | |||
self.name = self.file_name | |||
else: | |||
self.name = self.file_url | |||
self.name = frappe.generate_hash("", 10) | |||
def after_insert(self): | |||
self.update_parent_folder_size() | |||
@@ -53,7 +53,8 @@ class File(NestedSet): | |||
return frappe.db.sql_list("select name from tabFile where folder='%s'"%self.name) or [] | |||
def validate(self): | |||
self.validate_duplicate_entry() | |||
if self.is_new(): | |||
self.validate_duplicate_entry() | |||
self.validate_folder() | |||
self.set_folder_size() | |||
@@ -92,6 +93,8 @@ class File(NestedSet): | |||
def validate_duplicate_entry(self): | |||
if not self.flags.ignore_duplicate_entry_error and not self.is_folder: | |||
# check duplicate name | |||
# check duplicate assignement | |||
n_records = frappe.db.sql("""select name from `tabFile` | |||
where content_hash=%s | |||
@@ -111,6 +114,50 @@ class File(NestedSet): | |||
super(File, self).on_trash() | |||
self.delete_file() | |||
def make_thumbnail(self): | |||
from PIL import Image, ImageOps | |||
import os | |||
if self.file_url: | |||
if self.file_url.startswith("/files"): | |||
try: | |||
image = Image.open(frappe.get_site_path("public", self.file_url)) | |||
filename, extn = self.file_url.rsplit(".", 1) | |||
except IOError: | |||
frappe.msgprint("Unable to read file format for {0}".format(self.file_url)) | |||
else: | |||
# downlaod | |||
import requests, StringIO | |||
file_url = frappe.utils.get_url(self.file_url) | |||
r = requests.get(file_url, stream=True) | |||
r.raise_for_status() | |||
image = Image.open(StringIO.StringIO(r.content)) | |||
filename, extn = self.file_url.rsplit("/", 1)[1].rsplit(".", 1) | |||
filename = "/files/" + filename | |||
thumbnail = ImageOps.fit( | |||
image, | |||
(300, 300), | |||
Image.ANTIALIAS | |||
) | |||
thumbnail_url = filename + "_small." + extn | |||
if thumbnail_url[0]=="/": | |||
thumbnail_url = thumbnail_url[1:] | |||
path = os.path.abspath(frappe.get_site_path("public", thumbnail_url)) | |||
try: | |||
thumbnail.save(path) | |||
self.db_set("thumbnail_url", thumbnail_url) | |||
except IOError: | |||
frappe.msgprint("Unable to write file format for {0}".format(path)) | |||
return thumbnail_url | |||
def after_delete(self): | |||
self.update_parent_folder_size() | |||
@@ -178,7 +225,10 @@ def create_new_folder(file_name, folder): | |||
@frappe.whitelist() | |||
def move_file(file_list, new_parent, old_parent): | |||
for file_obj in json.loads(file_list): | |||
if isinstance(file_list, basestring): | |||
file_list = json.loads(file_list) | |||
for file_obj in file_list: | |||
setup_folder_path(file_obj.get("name"), new_parent) | |||
# recalculate sizes | |||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals | |||
import frappe | |||
import unittest | |||
from frappe.utils.file_manager import save_file, get_file, get_files_path | |||
from frappe.utils.file_manager import save_file, get_files_path | |||
from frappe import _ | |||
from frappe.core.doctype.file.file import move_file | |||
import json | |||
@@ -15,27 +15,19 @@ class TestFile(unittest.TestCase): | |||
def setUp(self): | |||
self.delete_test_data() | |||
self.upload_file() | |||
def delete_test_data(self): | |||
for file_name in ["folder_copy.txt", "file_copy.txt", "Test Folder 2"]: | |||
for file_name in ["folder_copy.txt", "file_copy.txt", "Test Folder 3", "Test Folder 2", "Test Folder 1"]: | |||
file_name = frappe.db.get_value("File", {"file_name": file_name}, "name") | |||
if file_name: | |||
file = frappe.get_doc("File", file_name) | |||
ancestors = file.get_ancestors() | |||
file.delete() | |||
self.delete_ancestors(ancestors) | |||
def delete_ancestors(self, ancestors): | |||
for folder in ancestors: | |||
if folder != "Home": | |||
folder = frappe.get_doc("File", folder) | |||
folder.delete() | |||
def upload_file(self): | |||
self.saved_file = save_file('file_copy.txt', "Testing file copy example.",\ | |||
"", "", self.get_folder("Test Folder 1", "Home").name) | |||
self.saved_filename = get_files_path(self.saved_file.file_name) | |||
def get_folder(self, folder_name, parent_folder="Home"): | |||
return frappe.get_doc({ | |||
"doctype": "File", | |||
@@ -46,61 +38,57 @@ class TestFile(unittest.TestCase): | |||
def tests_after_upload(self): | |||
self.assertEqual(self.saved_file.folder, _("Home/Test Folder 1")) | |||
folder_size = frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size") | |||
saved_file_size = frappe.db.get_value("File", self.saved_file.name, "file_size") | |||
self.assertEqual(folder_size, saved_file_size) | |||
def test_file_copy(self): | |||
folder = self.get_folder("Test Folder 2", "Home") | |||
file = frappe.get_doc("File", "/files/file_copy.txt") | |||
file_dict = [{"name": file.name}] | |||
move_file(json.dumps(file_dict), folder.name, file.folder) | |||
file = frappe.get_doc("File", "/files/file_copy.txt") | |||
file = frappe.get_doc("File", {"file_name":"file_copy.txt"}) | |||
move_file([{"name": file.name}], folder.name, file.folder) | |||
file = frappe.get_doc("File", {"file_name":"file_copy.txt"}) | |||
self.assertEqual(_("Home/Test Folder 2"), file.folder) | |||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 2"), "file_size"), file.file_size) | |||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), None) | |||
def test_folder_copy(self): | |||
folder = self.get_folder("Test Folder 2", "Home") | |||
folder = self.get_folder("Test Folder 3", "Home/Test Folder 2") | |||
self.saved_file = save_file('folder_copy.txt', "Testing folder copy example.", "", "", folder.name) | |||
file_dict = [{"name": folder.name}] | |||
move_file(json.dumps(file_dict), 'Home/Test Folder 1', folder.folder) | |||
file = frappe.get_doc("File", "/files/folder_copy.txt") | |||
move_file([{"name": folder.name}], 'Home/Test Folder 1', folder.folder) | |||
file = frappe.get_doc("File", {"file_name":"folder_copy.txt"}) | |||
file_copy_txt = frappe.get_value("File", {"file_name":"file_copy.txt"}) | |||
if file_copy_txt: | |||
frappe.get_doc("File", file_copy_txt).delete() | |||
self.assertEqual(_("Home/Test Folder 1/Test Folder 3"), file.folder) | |||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), file.file_size) | |||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 2"), "file_size"), None) | |||
def test_non_parent_folder(self): | |||
d = frappe.get_doc({ | |||
"doctype": "File", | |||
"file_name": _("Test_Folder"), | |||
"is_folder": 1 | |||
}) | |||
self.assertRaises(frappe.ValidationError, d.save) | |||
def test_on_delete(self): | |||
file = frappe.get_doc("File", "/files/file_copy.txt") | |||
file = frappe.get_doc("File", {"file_name":"file_copy.txt"}) | |||
file.delete() | |||
self.assertEqual(frappe.db.get_value("File", _("Home/Test Folder 1"), "file_size"), None) | |||
folder = self.get_folder("Test Folder 3", "Home/Test Folder 1") | |||
self.saved_file = save_file('folder_copy.txt', "Testing folder copy example.", "", "", folder.name) | |||
folder = frappe.get_doc("File", "Home/Test Folder 1/Test Folder 3") | |||
self.assertRaises(frappe.ValidationError, folder.delete) | |||
@@ -82,7 +82,7 @@ def make_autoname(key, doctype=''): | |||
DE/09/01/0001 where 09 is the year, 01 is the month and 0001 is the series | |||
""" | |||
if key=="hash": | |||
return frappe.generate_hash(doctype)[:10] | |||
return frappe.generate_hash(doctype, 10) | |||
if not "#" in key: | |||
key = key + ".#####" | |||
@@ -513,6 +513,9 @@ def get_url(uri=None, full_address=False): | |||
"""get app url from request""" | |||
host_name = frappe.local.conf.host_name | |||
if uri and (uri.startswith("http://") or uri.startswith("https://")): | |||
return uri | |||
if not host_name: | |||
if hasattr(frappe.local, "request") and frappe.local.request and frappe.local.request.host: | |||
protocol = 'https' == frappe.get_request_header('X-Forwarded-Proto', "") and 'https://' or 'http://' | |||
@@ -246,17 +246,18 @@ def delete_file_data_content(doc): | |||
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) | |||
path = encode(path) | |||
if os.path.exists(path): | |||
os.remove(path) | |||
"""Delete file, thumbnail from File document""" | |||
delete_file(doc.file_url) | |||
delete_file(doc.thumbnail_url) | |||
def delete_file(path): | |||
"""Delete file from `public folder`""" | |||
if path and path.startswith("/files/"): | |||
parts = os.path.split(path) | |||
path = frappe.utils.get_site_path("public", "files", parts[-1]) | |||
path = encode(path) | |||
if os.path.exists(path): | |||
os.remove(path) | |||
def get_file(fname): | |||
f = frappe.db.sql("""select file_name from `tabFile` | |||
@@ -30,3 +30,4 @@ click | |||
num2words | |||
watchdog==0.8.0 | |||
bleach | |||
Pillow |