@@ -93,52 +93,89 @@ def validate(self):
9393 self .set_file_name ()
9494 self .validate_duplicate_entry ()
9595 self .validate_attachment_limit ()
96+
9697 self .validate_folder ()
9798
98- if not self .file_url and not self .flags .ignore_file_validate :
99- if not self .is_folder :
99+ if self .is_folder :
100+ self .file_url = ""
101+ else :
102+ self .validate_url ()
103+
104+ self .file_size = frappe .form_dict .file_size or self .file_size
105+
106+ def validate_url (self ):
107+ if not self .file_url or self .file_url .startswith (("http://" , "https://" )):
108+ if not self .flags .ignore_file_validate :
100109 self .validate_file ()
101- self .generate_content_hash ()
102110
103- self . validate_url ()
111+ return
104112
105- if frappe .db .exists ('File' , {'name' : self .name , 'is_folder' : 0 }):
106- old_file_url = self .file_url
107- if not self .is_folder and (self .is_private != self .db_get ('is_private' )):
108- private_files = frappe .get_site_path ('private' , 'files' )
109- public_files = frappe .get_site_path ('public' , 'files' )
113+ # Probably an invalid web URL
114+ if not self .file_url .startswith (("/files/" , "/private/files/" )):
115+ frappe .throw (
116+ _ ("URL must start with http:// or https://" ),
117+ title = _ ('Invalid URL' )
118+ )
119+
120+ # Ensure correct formatting and type
121+ self .file_url = unquote (self .file_url )
122+ self .is_private = cint (self .is_private )
123+
124+ self .handle_is_private_changed ()
125+
126+ base_path = os .path .realpath (get_files_path (is_private = self .is_private ))
127+ if not os .path .realpath (self .get_full_path ()).startswith (base_path ):
128+ frappe .throw (
129+ _ ("The File URL you've entered is incorrect" ),
130+ title = _ ('Invalid File URL' )
131+ )
132+
133+ def handle_is_private_changed (self ):
134+ if not frappe .db .exists (
135+ 'File' , {
136+ 'name' : self .name ,
137+ 'is_private' : cint (not self .is_private )
138+ }
139+ ):
140+ return
110141
111- file_name = self .file_url .split ('/' )[- 1 ]
112- if not self .is_private :
113- shutil .move (os .path .join (private_files , file_name ),
114- os .path .join (public_files , file_name ))
142+ old_file_url = self .file_url
115143
116- self .file_url = "/files/{0}" .format (file_name )
144+ file_name = self .file_url .split ('/' )[- 1 ]
145+ private_file_path = frappe .get_site_path ('private' , 'files' , file_name )
146+ public_file_path = frappe .get_site_path ('public' , 'files' , file_name )
117147
118- else :
119- shutil .move (os .path .join (public_files , file_name ),
120- os .path .join (private_files , file_name ))
148+ if self .is_private :
149+ shutil .move (public_file_path , private_file_path )
150+ url_starts_with = "/private/files/"
151+ else :
152+ shutil .move (private_file_path , public_file_path )
153+ url_starts_with = "/files/"
121154
122- self .file_url = "/private/files/{0}" .format (file_name )
155+ self .file_url = "{0}{1}" .format (url_starts_with , file_name )
156+ update_existing_file_docs (self )
123157
124- update_existing_file_docs (self )
158+ if (
159+ not self .attached_to_doctype
160+ or not self .attached_to_name
161+ or not self .fetch_attached_to_field (old_file_url )
162+ ):
163+ return
125164
126- # update documents image url with new file url
127- if self .attached_to_doctype and self .attached_to_name :
128- if not self .attached_to_field :
129- field_name = None
130- reference_dict = frappe .get_doc (self .attached_to_doctype , self .attached_to_name ).as_dict ()
131- for key , value in reference_dict .items ():
132- if value == old_file_url :
133- field_name = key
134- break
135- self .attached_to_field = field_name
136- if self .attached_to_field :
137- frappe .db .set_value (self .attached_to_doctype , self .attached_to_name ,
138- self .attached_to_field , self .file_url )
139-
140- if self .file_url and (self .is_private != self .file_url .startswith ('/private' )):
141- frappe .throw (_ ('Invalid file URL. Please contact System Administrator.' ))
165+ frappe .db .set_value (self .attached_to_doctype , self .attached_to_name ,
166+ self .attached_to_field , self .file_url )
167+
168+ def fetch_attached_to_field (self , old_file_url ):
169+ if self .attached_to_field :
170+ return True
171+
172+ reference_dict = frappe .get_doc (
173+ self .attached_to_doctype , self .attached_to_name ).as_dict ()
174+
175+ for key , value in reference_dict .items ():
176+ if value == old_file_url :
177+ self .attached_to_field = key
178+ return True
142179
143180 def validate_attachment_limit (self ):
144181 attachment_limit = 0
@@ -331,8 +368,13 @@ def exists_on_disk(self):
331368
332369 def get_content (self ):
333370 """Returns [`file_name`, `content`] for given file name `fname`"""
371+ if self .is_folder :
372+ frappe .throw (_ ("Cannot get file contents of a Folder" ))
373+
334374 if self .get ('content' ):
335375 return self .content
376+
377+ self .validate_url ()
336378 file_path = self .get_full_path ()
337379
338380 # read the file
@@ -419,17 +461,6 @@ def save_uploaded(self):
419461 else :
420462 raise Exception
421463
422-
423- def validate_url (self , df = None ):
424- if self .file_url :
425- if not self .file_url .startswith (("http://" , "https://" , "/files/" , "/private/files/" )):
426- frappe .throw (_ ("URL must start with 'http://' or 'https://'" ))
427- return
428-
429- self .file_url = unquote (self .file_url )
430- self .file_size = frappe .form_dict .file_size or self .file_size
431-
432-
433464 def get_uploaded_content (self ):
434465 # should not be unicode when reading a file, hence using frappe.form
435466 if 'filedata' in frappe .form_dict :
@@ -692,7 +723,7 @@ def delete_file(path):
692723 os .remove (path )
693724
694725
695- def remove_file (fid = None , attached_to_doctype = None , attached_to_name = None , from_delete = False ):
726+ def remove_file (fid = None , attached_to_doctype = None , attached_to_name = None , from_delete = False , delete_permanently = False ):
696727 """Remove file and File entry"""
697728 file_name = None
698729 if not (attached_to_doctype and attached_to_name ):
@@ -710,7 +741,7 @@ def remove_file(fid=None, attached_to_doctype=None, attached_to_name=None, from_
710741 if not file_name :
711742 file_name = frappe .db .get_value ("File" , fid , "file_name" )
712743 comment = doc .add_comment ("Attachment Removed" , _ ("Removed {0}" ).format (file_name ))
713- frappe .delete_doc ("File" , fid , ignore_permissions = ignore_permissions )
744+ frappe .delete_doc ("File" , fid , ignore_permissions = ignore_permissions , delete_permanently = delete_permanently )
714745
715746 return comment
716747
@@ -719,17 +750,18 @@ def get_max_file_size():
719750 return conf .get ('max_file_size' ) or 10485760
720751
721752
722- def remove_all (dt , dn , from_delete = False ):
753+ def remove_all (dt , dn , from_delete = False , delete_permanently = False ):
723754 """remove all files in a transaction"""
724755 try :
725756 for fid in frappe .db .sql_list ("""select name from `tabFile` where
726757 attached_to_doctype=%s and attached_to_name=%s""" , (dt , dn )):
727758 if from_delete :
728759 # If deleting a doc, directly delete files
729- frappe .delete_doc ("File" , fid , ignore_permissions = True )
760+ frappe .delete_doc ("File" , fid , ignore_permissions = True , delete_permanently = delete_permanently )
730761 else :
731762 # Removes file and adds a comment in the document it is attached to
732- remove_file (fid = fid , attached_to_doctype = dt , attached_to_name = dn , from_delete = from_delete )
763+ remove_file (fid = fid , attached_to_doctype = dt , attached_to_name = dn ,
764+ from_delete = from_delete , delete_permanently = delete_permanently )
733765 except Exception as e :
734766 if e .args [0 ]!= 1054 : raise # (temp till for patched)
735767
0 commit comments