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.
 
 
 
 
 
 

200 line
5.5 KiB

  1. # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import webnotes
  5. import os
  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. import base64
  48. webnotes.uploaded_content = base64.b64decode(webnotes.form_dict.filedata)
  49. webnotes.uploaded_filename = webnotes.form_dict.filename
  50. return webnotes.uploaded_filename, webnotes.uploaded_content
  51. else:
  52. webnotes.msgprint('No File')
  53. return None, None
  54. def save_file(fname, content, dt, dn):
  55. import filecmp
  56. from webnotes.model.code import load_doctype_module
  57. files_path = get_site_path(conf.public_path)
  58. module = load_doctype_module(dt, webnotes.conn.get_value("DocType", dt, "module"))
  59. if hasattr(module, "attachments_folder"):
  60. files_path = os.path.join(files_path, module.attachments_folder)
  61. file_size = check_max_file_size(content)
  62. temp_fname = write_file(content, files_path)
  63. fname = scrub_file_name(fname)
  64. fpath = os.path.join(files_path, fname)
  65. fname_parts = fname.split(".", -1)
  66. main = ".".join(fname_parts[:-1])
  67. extn = fname_parts[-1]
  68. versions = get_file_versions(files_path, main, extn)
  69. if versions:
  70. found_match = False
  71. for version in versions:
  72. if filecmp.cmp(os.path.join(files_path, version), temp_fname):
  73. # remove new file, already exists!
  74. os.remove(temp_fname)
  75. fname = version
  76. found_match = True
  77. break
  78. if not found_match:
  79. # get_new_version name
  80. fname = get_new_fname_based_on_version(files_path, main, extn, versions)
  81. # rename
  82. os.rename(temp_fname, os.path.join(files_path, fname))
  83. else:
  84. # rename new file
  85. os.rename(temp_fname, os.path.join(files_path, fname))
  86. f = webnotes.bean({
  87. "doctype": "File Data",
  88. "file_name": os.path.relpath(os.path.join(files_path, fname), get_site_path(conf.public_path)),
  89. "attached_to_doctype": dt,
  90. "attached_to_name": dn,
  91. "file_size": file_size
  92. })
  93. f.ignore_permissions = True
  94. f.insert();
  95. return f.doc
  96. def get_file_versions(files_path, main, extn):
  97. return filter(lambda f: f.startswith(main) and f.endswith(extn), os.listdir(files_path))
  98. def get_new_fname_based_on_version(files_path, main, extn, versions):
  99. versions.sort()
  100. if "-" in versions[-1]:
  101. version = cint(versions[-1].split("-")[-1]) or 1
  102. else:
  103. version = 1
  104. new_fname = main + "-" + str(version) + "." + extn
  105. while os.path.exists(os.path.join(files_path, new_fname)):
  106. version += 1
  107. new_fname = main + "-" + str(version) + "." + extn
  108. if version > 100:
  109. webnotes.msgprint("Too many versions", raise_exception=True)
  110. return new_fname
  111. def scrub_file_name(fname):
  112. if '\\' in fname:
  113. fname = fname.split('\\')[-1]
  114. if '/' in fname:
  115. fname = fname.split('/')[-1]
  116. return fname
  117. def check_max_file_size(content):
  118. max_file_size = conf.get('max_file_size') or 1000000
  119. file_size = len(content)
  120. if file_size > max_file_size:
  121. webnotes.msgprint(_("File size exceeded the maximum allowed size"),
  122. raise_exception=MaxFileSizeReachedError)
  123. return file_size
  124. def write_file(content, files_path):
  125. """write file to disk with a random name (to compare)"""
  126. # create account folder (if not exists)
  127. webnotes.create_folder(files_path)
  128. fname = os.path.join(files_path, webnotes.generate_hash())
  129. # write the file
  130. with open(fname, 'w+') as f:
  131. f.write(content)
  132. return fname
  133. def remove_all(dt, dn):
  134. """remove all files in a transaction"""
  135. try:
  136. for fid in webnotes.conn.sql_list("""select name from `tabFile Data` where
  137. attached_to_doctype=%s and attached_to_name=%s""", (dt, dn)):
  138. remove_file(fid)
  139. except Exception, e:
  140. if e.args[0]!=1054: raise e # (temp till for patched)
  141. def remove_file(fid):
  142. """Remove file and File Data entry"""
  143. webnotes.delete_doc("File Data", fid)
  144. def get_file(fname):
  145. f = webnotes.conn.sql("""select file_name from `tabFile Data`
  146. where name=%s or file_name=%s""", (fname, fname))
  147. if f:
  148. file_name = f[0][0]
  149. else:
  150. file_name = fname
  151. # read the file
  152. import os
  153. files_path = get_site_path(conf.files_path)
  154. file_path = os.path.join(files_path, file_name)
  155. if not os.path.exists(file_path):
  156. # check in folders
  157. for basepath, folders, files in os.walk(files_path):
  158. if file_name in files:
  159. file_path = os.path.join(basepath, file_name)
  160. break
  161. with open(file_path, 'r') as f:
  162. content = f.read()
  163. return [file_name, content]