You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

240 rivejä
6.8 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import webnotes
  5. import os, base64, re
  6. from webnotes.utils import cstr, cint, get_site_path
  7. from webnotes import _
  8. from webnotes import conf
  9. class MaxFileSizeReachedError(webnotes.ValidationError): pass
  10. def get_file_url(file_data_name):
  11. data = webnotes.conn.get_value("File Data", file_data_name, ["file_name", "file_url"], as_dict=True)
  12. return data.file_name or data.file_url
  13. def upload():
  14. # get record details
  15. dt = webnotes.form_dict.doctype
  16. dn = webnotes.form_dict.docname
  17. file_url = webnotes.form_dict.file_url
  18. filename = webnotes.form_dict.filename
  19. if not filename and not file_url:
  20. webnotes.msgprint(_("Please select a file or url"),
  21. raise_exception=True)
  22. # save
  23. if filename:
  24. filedata = save_uploaded(dt, dn)
  25. elif file_url:
  26. filedata = save_url(file_url, dt, dn)
  27. return {"fid": filedata.name, "filename": filedata.file_name or filedata.file_url }
  28. def save_uploaded(dt, dn):
  29. fname, content = get_uploaded_content()
  30. if content:
  31. return save_file(fname, content, dt, dn);
  32. else:
  33. raise Exception
  34. def save_url(file_url, dt, dn):
  35. if not (file_url.startswith("http://") or file_url.startswith("https://")):
  36. webnotes.msgprint("URL must start with 'http://' or 'https://'")
  37. return None, None
  38. f = webnotes.bean({
  39. "doctype": "File Data",
  40. "file_url": file_url,
  41. "attached_to_doctype": dt,
  42. "attached_to_name": dn
  43. })
  44. f.ignore_permissions = True
  45. try:
  46. f.insert();
  47. except webnotes.DuplicateEntryError:
  48. return webnotes.doc("File Data", f.doc.duplicate_entry)
  49. return f.doc
  50. def get_uploaded_content():
  51. # should not be unicode when reading a file, hence using webnotes.form
  52. if 'filedata' in webnotes.form_dict:
  53. webnotes.uploaded_content = base64.b64decode(webnotes.form_dict.filedata)
  54. webnotes.uploaded_filename = webnotes.form_dict.filename
  55. return webnotes.uploaded_filename, webnotes.uploaded_content
  56. else:
  57. webnotes.msgprint('No File')
  58. return None, None
  59. def extract_images_from_html(doc, fieldname):
  60. content = doc.get(fieldname)
  61. webnotes.flags.has_dataurl = False
  62. def _save_file(match):
  63. data = match.group(1)
  64. headers, content = data.split(",")
  65. filename = headers.split("filename=")[-1]
  66. filename = save_file(filename, content, doc.doctype, doc.name, decode=True).get("file_name")
  67. if not webnotes.flags.has_dataurl:
  68. webnotes.flags.has_dataurl = True
  69. return '<img src="{filename}"'.format(filename = filename)
  70. if content:
  71. content = re.sub('<img\s*src=\s*["\'](data:[^"\']*)["\']', _save_file, content)
  72. if webnotes.flags.has_dataurl:
  73. doc.fields[fieldname] = content
  74. def save_file(fname, content, dt, dn, decode=False):
  75. if decode:
  76. if isinstance(content, unicode):
  77. content = content.encode("utf-8")
  78. content = base64.b64decode(content)
  79. import filecmp
  80. from webnotes.model.code import load_doctype_module
  81. files_path = os.path.join(webnotes.local.site_path, "public", "files")
  82. module = load_doctype_module(dt, webnotes.conn.get_value("DocType", dt, "module"))
  83. if hasattr(module, "attachments_folder"):
  84. files_path = os.path.join(files_path, module.attachments_folder)
  85. file_size = check_max_file_size(content)
  86. temp_fname = write_file(content, files_path)
  87. fname = scrub_file_name(fname)
  88. fname_parts = fname.split(".", -1)
  89. main = ".".join(fname_parts[:-1])
  90. extn = fname_parts[-1]
  91. versions = get_file_versions(files_path, main, extn)
  92. if versions:
  93. found_match = False
  94. for version in versions:
  95. if filecmp.cmp(os.path.join(files_path, version), temp_fname):
  96. # remove new file, already exists!
  97. os.remove(temp_fname)
  98. fname = version
  99. fpath = os.path.join(files_path, fname)
  100. found_match = True
  101. break
  102. if not found_match:
  103. # get_new_version name
  104. fname = get_new_fname_based_on_version(files_path, main, extn, versions)
  105. fpath = os.path.join(files_path, fname)
  106. # rename
  107. if os.path.exists(fpath.encode("utf-8")):
  108. webnotes.throw("File already exists: " + fname)
  109. os.rename(temp_fname, fpath.encode("utf-8"))
  110. else:
  111. fpath = os.path.join(files_path, fname)
  112. # rename new file
  113. if os.path.exists(fpath.encode("utf-8")):
  114. webnotes.throw("File already exists: " + fname)
  115. os.rename(temp_fname, fpath.encode("utf-8"))
  116. f = webnotes.bean({
  117. "doctype": "File Data",
  118. "file_name": os.path.relpath(os.path.join(files_path, fname), get_site_path("public")),
  119. "attached_to_doctype": dt,
  120. "attached_to_name": dn,
  121. "file_size": file_size
  122. })
  123. f.ignore_permissions = True
  124. try:
  125. f.insert();
  126. except webnotes.DuplicateEntryError:
  127. return webnotes.doc("File Data", f.doc.duplicate_entry)
  128. return f.doc
  129. def get_file_versions(files_path, main, extn):
  130. out = []
  131. for f in os.listdir(files_path):
  132. f = cstr(f)
  133. if f.startswith(main) and f.endswith(extn):
  134. out.append(f)
  135. return out
  136. def get_new_fname_based_on_version(files_path, main, extn, versions):
  137. versions.sort()
  138. if "-" in versions[-1]:
  139. version = cint(versions[-1].split("-")[-1]) or 1
  140. else:
  141. version = 1
  142. new_fname = main + "-" + str(version) + "." + extn
  143. while os.path.exists(os.path.join(files_path, new_fname).encode("utf-8")):
  144. version += 1
  145. new_fname = main + "-" + str(version) + "." + extn
  146. if version > 100:
  147. webnotes.msgprint("Too many versions", raise_exception=True)
  148. return new_fname
  149. def scrub_file_name(fname):
  150. if '\\' in fname:
  151. fname = fname.split('\\')[-1]
  152. if '/' in fname:
  153. fname = fname.split('/')[-1]
  154. return fname
  155. def check_max_file_size(content):
  156. max_file_size = conf.get('max_file_size') or 1000000
  157. file_size = len(content)
  158. if file_size > max_file_size:
  159. webnotes.msgprint(_("File size exceeded the maximum allowed size"),
  160. raise_exception=MaxFileSizeReachedError)
  161. return file_size
  162. def write_file(content, files_path):
  163. """write file to disk with a random name (to compare)"""
  164. # create account folder (if not exists)
  165. webnotes.create_folder(files_path)
  166. fname = os.path.join(files_path, webnotes.generate_hash())
  167. # write the file
  168. with open(fname, 'w+') as f:
  169. f.write(content)
  170. return fname
  171. def remove_all(dt, dn):
  172. """remove all files in a transaction"""
  173. try:
  174. for fid in webnotes.conn.sql_list("""select name from `tabFile Data` where
  175. attached_to_doctype=%s and attached_to_name=%s""", (dt, dn)):
  176. remove_file(fid)
  177. except Exception, e:
  178. if e.args[0]!=1054: raise # (temp till for patched)
  179. def remove_file(fid):
  180. """Remove file and File Data entry"""
  181. webnotes.delete_doc("File Data", fid)
  182. def get_file(fname):
  183. f = webnotes.conn.sql("""select file_name from `tabFile Data`
  184. where name=%s or file_name=%s""", (fname, fname))
  185. if f:
  186. file_name = f[0][0]
  187. else:
  188. file_name = fname
  189. if not "/" in file_name:
  190. file_name = "files/" + file_name
  191. # read the file
  192. with open(get_site_path("public", file_name), 'r') as f:
  193. content = f.read()
  194. return [file_name, content]