def put(self, item_id, folder_id='0'): """ :param item_id: :param folder_id: id of the folder, in a style like 'workspace_14__content_1586' :return: """ # TODO - SECURE THIS workspace = tmpl_context.workspace item_id = int(item_id) new_workspace, new_parent = convert_id_into_instances(folder_id) if new_workspace != workspace: # check that user is at least # - content manager in current workspace # - content manager in new workspace user = tmpl_context.current_user if user.get_role(workspace) < UserRoleInWorkspace.CONTENT_MANAGER: tg.flash(_('You are not allowed ' 'to move this folder'), CST.STATUS_ERROR) tg.redirect(self.parent_controller.url(item_id)) if user.get_role(new_workspace) < UserRoleInWorkspace.CONTENT_MANAGER: tg.flash(_('You are not allowed to move ' 'this folder to this workspace'), CST.STATUS_ERROR) tg.redirect(self.parent_controller.url(item_id)) api = ContentApi(tmpl_context.current_user) item = api.get_one(item_id, ContentType.Any, workspace) api.move_recursively(item, new_parent, new_workspace) next_url = tg.url('/workspaces/{}/folders/{}'.format( new_workspace.workspace_id, item_id)) if new_parent: tg.flash(_('Item moved to {} (workspace {})').format( new_parent.label, new_workspace.label), CST.STATUS_OK) else: tg.flash(_('Item moved to workspace {}').format( new_workspace.label)) tg.redirect(next_url) else: # Default move inside same workspace api = ContentApi(tmpl_context.current_user) item = api.get_one(item_id, ContentType.Any, workspace) api.move(item, new_parent) next_url = self.parent_controller.url(item_id) if new_parent: tg.flash(_('Item moved to {}').format(new_parent.label), CST.STATUS_OK) else: tg.flash(_('Item moved to workspace root')) tg.redirect(next_url)
def put(self, item_id, folder_id='0'): """ :param item_id: :param folder_id: id of the folder, in a style like 'workspace_14__content_1586' :return: """ # TODO - SECURE THIS workspace = tmpl_context.workspace item_id = int(item_id) new_workspace, new_parent = convert_id_into_instances(folder_id) if new_workspace != workspace: # check that user is at least # - content manager in current workspace # - content manager in new workspace user = tmpl_context.current_user if user.get_role(workspace) < UserRoleInWorkspace.CONTENT_MANAGER: tg.flash(_('You are not allowed ' 'to move this folder'), CST.STATUS_ERROR) tg.redirect(self.parent_controller.url(item_id)) if user.get_role( new_workspace) < UserRoleInWorkspace.CONTENT_MANAGER: tg.flash( _('You are not allowed to move ' 'this folder to this workspace'), CST.STATUS_ERROR) tg.redirect(self.parent_controller.url(item_id)) api = ContentApi(tmpl_context.current_user) item = api.get_one(item_id, ContentType.Any, workspace) with new_revision(item): api.move_recursively(item, new_parent, new_workspace) next_url = tg.url('/workspaces/{}/folders/{}'.format( new_workspace.workspace_id, item_id)) if new_parent: tg.flash( _('Item moved to {} (workspace {})').format( new_parent.label, new_workspace.label), CST.STATUS_OK) else: tg.flash( _('Item moved to workspace {}').format( new_workspace.label)) tg.redirect(next_url) else: # Default move inside same workspace api = ContentApi(tmpl_context.current_user) item = api.get_one(item_id, ContentType.Any, workspace) with new_revision(item): api.move(item, new_parent) next_url = self.parent_controller.url(item_id) if new_parent: tg.flash( _('Item moved to {}').format(new_parent.label), CST.STATUS_OK) else: tg.flash(_('Item moved to workspace root')) tg.redirect(next_url)
class File(DAVNonCollection): """ File resource corresponding to tracim's files """ def __init__(self, path: str, environ: dict, content: Content): super(File, self).__init__(path, environ) self.content = content self.user = UserApi(None).get_one_by_email(environ['http_authenticator.username']) self.content_api = ContentApi(self.user) # 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: File (%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 ) 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( ActionDescription.UNDELETION if self.content.is_deleted else ActionDescription.UNARCHIVING, self.content_api, self.content ).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( ActionDescription.DELETION if '.deleted' in destpath else ActionDescription.ARCHIVING, self.content_api, self.content ).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): workspace = self.content.workspace parent = self.content.parent with new_revision(self.content): 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) else: workspace_api = WorkspaceApi(self.user) content_api = ContentApi(self.user) destination_workspace = self.provider.get_workspace_from_path( destpath, workspace_api, ) destination_parent = self.provider.get_parent_from_path( destpath, content_api, destination_workspace, ) self.content_api.move( item=self.content, new_parent=destination_parent, must_stay_in_same_workspace=False, new_workspace=destination_workspace ) transaction.commit() def supportRecursiveMove(self, destPath): return True def delete(self): ManageActions(ActionDescription.DELETION, self.content_api, self.content).action()
class File(DAVNonCollection): """ File resource corresponding to tracim's files """ def __init__(self, path: str, environ: dict, content: Content): super(File, self).__init__(path, environ) self.content = content self.user = UserApi(None).get_one_by_email( environ['http_authenticator.username']) self.content_api = ContentApi(self.user) # 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 getPreferredPath(self): fix_txt = '.txt' if self.getContentType( ) == 'text/plain' else mimetypes.guess_extension(self.getContentType()) if self.content.label == '' or self.path.endswith(fix_txt): return self.path else: return self.path + fix_txt def __repr__(self) -> str: return "<DAVNonCollection: File (%d)>" % self.content.revision_id def getContentLength(self) -> int: return len(self.content.file_content) 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.get_label() def getLastModified(self) -> float: return mktime(self.content.updated.timetuple()) def getContent(self): filestream = compat.BytesIO() filestream.write(self.content.file_content) 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(), workspace=self.content.workspace, path=self.path) 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( ActionDescription.UNDELETION if self.content.is_deleted else ActionDescription.UNARCHIVING, self.content_api, self.content).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( ActionDescription.DELETION if '.deleted' in destpath else ActionDescription.ARCHIVING, self.content_api, self.content).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): workspace = self.provider.get_workspace_from_path( normpath(destpath), WorkspaceApi(self.user)) parent = self.provider.get_parent_from_path(normpath(destpath), self.content_api, workspace) with new_revision(self.content): if basename(destpath) != self.getDisplayName(): self.content_api.update_content( self.content, re.sub('\.[^\.]+$', '', self.provider.transform_to_bdd(basename(destpath)))) self.content_api.save(self.content) else: self.content_api.move(item=self.content, new_parent=parent, must_stay_in_same_workspace=False, new_workspace=workspace) transaction.commit() def supportRecursiveMove(self, destPath): return True def delete(self): ManageActions(ActionDescription.DELETION, self.content_api, self.content).action()