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.
 
 
 
 
 
 

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