def move_content( self, context, request: TracimRequest, hapic_data=None, ) -> ContentInContext: """ move a content """ app_config = request.registry.settings['CFG'] path_data = hapic_data.path move_data = hapic_data.body api = ContentApi( current_user=request.current_user, session=request.dbsession, config=app_config, ) content = api.get_one( path_data.content_id, content_type=ContentType.Any ) new_parent = api.get_one( move_data.new_parent_id, content_type=ContentType.Any ) new_workspace = request.candidate_workspace with new_revision( session=request.dbsession, tm=transaction.manager, content=content ): api.move( content, new_parent=new_parent, new_workspace=new_workspace, must_stay_in_same_workspace=False, ) updated_content = api.get_one( path_data.content_id, content_type=ContentType.Any ) return api.get_content_in_context(updated_content)
class FileResource(DAVNonCollection): """ FileResource resource corresponding to tracim's files """ def __init__( self, path: str, environ: dict, content: Content, user: User, session: Session, ) -> None: super(FileResource, self).__init__(path, environ) self.content = content self.user = user self.session = session self.content_api = ContentApi( current_user=self.user, config=self.provider.app_config, session=self.session, ) # this is the property that windows client except to check if the file is read-write or read-only, # but i wasn't able to set this property so you'll have to look into it >.> # self.setPropertyValue('Win32FileAttributes', '00000021') def __repr__(self) -> str: return "<DAVNonCollection: FileResource (%d)>" % self.content.revision_id def getContentLength(self) -> int: return self.content.depot_file.file.content_length def getContentType(self) -> str: return self.content.file_mimetype def getCreationDate(self) -> float: return mktime(self.content.created.timetuple()) def getDisplayName(self) -> str: return self.content.file_name def getLastModified(self) -> float: return mktime(self.content.updated.timetuple()) def getContent(self) -> typing.BinaryIO: filestream = compat.BytesIO() filestream.write(self.content.depot_file.file.read()) filestream.seek(0) return filestream def beginWrite(self, contentType: str=None) -> FakeFileStream: return FakeFileStream( content=self.content, content_api=self.content_api, file_name=self.content.get_label_as_file(), workspace=self.content.workspace, path=self.path, session=self.session, ) def moveRecursive(self, destpath): """As we support recursive move, copymovesingle won't be called, though with copy it'll be called but i have to check if the client ever call that function...""" destpath = normpath(destpath) invalid_path = False # if content is either deleted or archived, we'll check that we try moving it to the parent # if yes, then we'll unarchive / undelete them, else the action's not allowed if self.content.is_deleted or self.content.is_archived: # we remove all archived and deleted from the path and we check to the destpath # has to be equal or else path not valid # ex: /a/b/.deleted/resource, to be valid destpath has to be = /a/b/resource (no other solution) current_path = re.sub(r'/\.(deleted|archived)', '', self.path) if current_path == destpath: ManageActions( action_type=ActionDescription.UNDELETION if self.content.is_deleted else ActionDescription.UNARCHIVING, api=self.content_api, content=self.content, session=self.session, ).action() else: invalid_path = True # if the content is not deleted / archived, check if we're trying to delete / archive it by # moving it to a .deleted / .archived folder elif basename(dirname(destpath)) in ['.deleted', '.archived']: # same test as above ^ dest_path = re.sub(r'/\.(deleted|archived)', '', destpath) if dest_path == self.path: ManageActions( action_type=ActionDescription.DELETION if '.deleted' in destpath else ActionDescription.ARCHIVING, api=self.content_api, content=self.content, session=self.session, ).action() else: invalid_path = True # else we check if the path is good (not at the root path / not in a deleted/archived path) # and we move the content else: invalid_path = any(x in destpath for x in ['.deleted', '.archived']) invalid_path = invalid_path or any(x in self.path for x in ['.deleted', '.archived']) invalid_path = invalid_path or dirname(destpath) == self.environ['http_authenticator.realm'] if not invalid_path: self.move_file(destpath) if invalid_path: raise DAVError(HTTP_FORBIDDEN) def move_file(self, destpath: str) -> None: """ Move file mean changing the path to access to a file. This can mean simple renaming(1), moving file from a directory to one another(2) but also renaming + moving file from a directory to one another at the same time (3). (1): move /dir1/file1 -> /dir1/file2 (2): move /dir1/file1 -> /dir2/file1 (3): move /dir1/file1 -> /dir2/file2 :param destpath: destination path of webdav move :return: nothing """ workspace = self.content.workspace parent = self.content.parent with new_revision( content=self.content, tm=transaction.manager, session=self.session, ): # INFO - G.M - 2018-03-09 - First, renaming file if needed if basename(destpath) != self.getDisplayName(): new_given_file_name = transform_to_bdd(basename(destpath)) new_file_name, new_file_extension = \ os.path.splitext(new_given_file_name) self.content_api.update_content( self.content, new_file_name, ) self.content.file_extension = new_file_extension self.content_api.save(self.content) # INFO - G.M - 2018-03-09 - Moving file if needed workspace_api = WorkspaceApi( current_user=self.user, session=self.session, config=self.provider.app_config, ) content_api = ContentApi( current_user=self.user, session=self.session, config=self.provider.app_config ) destination_workspace = self.provider.get_workspace_from_path( destpath, workspace_api, ) destination_parent = self.provider.get_parent_from_path( destpath, content_api, destination_workspace, ) if destination_parent != parent or destination_workspace != workspace: # nopep8 # INFO - G.M - 12-03-2018 - Avoid moving the file "at the same place" # nopep8 # if the request does not result in a real move. self.content_api.move( item=self.content, new_parent=destination_parent, must_stay_in_same_workspace=False, new_workspace=destination_workspace ) transaction.commit() def copyMoveSingle(self, destpath, isMove): if isMove: # INFO - G.M - 12-03-2018 - This case should not happen # As far as moveRecursive method exist, all move should not go # through this method. If such case appear, try replace this to : #### # self.move_file(destpath) # return #### raise NotImplemented new_file_name = None new_file_extension = None # Inspect destpath if basename(destpath) != self.getDisplayName(): new_given_file_name = transform_to_bdd(basename(destpath)) new_file_name, new_file_extension = \ os.path.splitext(new_given_file_name) workspace_api = WorkspaceApi( current_user=self.user, session=self.session, config=self.provider.app_config, ) content_api = ContentApi( current_user=self.user, session=self.session, config=self.provider.app_config ) destination_workspace = self.provider.get_workspace_from_path( destpath, workspace_api, ) destination_parent = self.provider.get_parent_from_path( destpath, content_api, destination_workspace, ) workspace = self.content.workspace parent = self.content.parent new_content = self.content_api.copy( item=self.content, new_label=new_file_name, new_parent=destination_parent, ) self.content_api.copy_children(self.content, new_content) transaction.commit() def supportRecursiveMove(self, destPath): return True def delete(self): ManageActions( action_type=ActionDescription.DELETION, api=self.content_api, content=self.content, session=self.session, ).action()