123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166 |
- # coding: utf8
-
- import transaction
- from wsgidav import util
- from wsgidav import compat
-
- from tracim.lib.content import ContentApi
- from tracim.lib.utils import SameValueError
- from tracim.lib.base import logger
- from tracim.model import new_revision
- from tracim.model.data import ActionDescription
- from tracim.model.data import ContentType
- from tracim.model.data import Content
- from tracim.model.data import Workspace
- from wsgidav.dav_error import DAVError, HTTP_FORBIDDEN, HTTP_NOT_MODIFIED
-
-
- class HistoryType(object):
- Deleted = 'deleted'
- Archived = 'archived'
- Standard = 'standard'
- All = 'all'
-
-
- class SpecialFolderExtension(object):
- Deleted = '/.deleted'
- Archived = '/.archived'
- History = '/.history'
-
-
- class FakeFileStream(object):
- """
- Fake a FileStream that we're giving to wsgidav to receive data and create files / new revisions
-
- There's two scenarios :
- - when a new file is created, wsgidav will call the method createEmptyResource and except to get a _DAVResource
- which should have both 'beginWrite' and 'endWrite' method implemented
- - when a file which already exists is updated, he's going to call the 'beginWrite' function of the _DAVResource
- to get a filestream and write content in it
-
- In the first case scenario, the transfer takes two part : it first create the resource (createEmptyResource)
- then add its content (beginWrite, write, close..). If we went without this class, we would create two revision
- of the file upon creating a new file, which is not what we want.
- """
-
- def __init__(self, content_api: ContentApi, workspace: Workspace, path: str,
- file_name: str='', content: Content=None, parent: Content=None):
- """
-
- :param content_api:
- :param workspace:
- :param path:
- :param file_name:
- :param content:
- :param parent:
- """
- self._file_stream = compat.BytesIO()
-
- self._file_name = file_name if file_name != '' else self._content.file_name
- self._content = content
- self._api = content_api
- self._workspace = workspace
- self._parent = parent
- self._path = path
-
- def getRefUrl(self) -> str:
- """
- As wsgidav expect to receive a _DAVResource upon creating a new resource, this method's result is used
- by Windows client to establish both file's path and file's name
- """
- return self._path
-
- def beginWrite(self, contentType) -> 'FakeFileStream':
- """
- Called by wsgidav, it expect a filestream which possess both 'write' and 'close' operation to write
- the file content.
- """
- return self
-
- def endWrite(self, withErrors: bool):
- """
- Called by request_server when finished writing everything.
- As we call operation to create new content or revision in the close operation, called before endWrite, there
- is nothing to do here.
- """
- pass
-
- def write(self, s: str):
- """
- Called by request_server when writing content to files, we put it inside a filestream
- """
- self._file_stream.write(s)
-
- def close(self):
- """
- Called by request_server when the file content has been written. We either add a new content or create
- a new revision
- """
-
- self._file_stream.seek(0)
-
- try:
- if self._content is None:
- self.create_file()
- else:
- self.update_file()
- transaction.commit()
- except SameValueError:
- # INFO - G.M - 21-03-2018 - Do nothing, file as not change.
- msg = 'File {filename} modified through webdav did not change, transaction aborted.' # nopep8
- logger.debug(
- self,
- msg.format(filename=self._file_name)
- )
- transaction.abort()
- transaction.begin()
- except ValueError as e:
- msg = 'File {filename} modified through webdav can\'t be updated: {exception}' # nopep8
- logger.debug(
- self,
- msg.format(
- filename=self._file_name,
- e=e.__str__,
- )
- )
- transaction.abort()
- transaction.begin()
- raise DAVError(HTTP_FORBIDDEN)
-
- def create_file(self):
- """
- Called when this is a new file; will create a new Content initialized with the correct content
- """
-
- is_temporary = self._file_name.startswith('.~') or self._file_name.startswith('~')
-
- file = self._api.create(
- content_type=ContentType.File,
- workspace=self._workspace,
- parent=self._parent,
- is_temporary=is_temporary
- )
-
- self._api.update_file_data(
- file,
- self._file_name,
- util.guessMimeType(self._file_name),
- self._file_stream.read()
- )
-
- self._api.save(file, ActionDescription.CREATION)
-
- def update_file(self):
- """
- Called when we're updating an existing content; we create a new revision and update the file content
- """
-
- with new_revision(self._content):
- self._api.update_file_data(
- self._content,
- self._file_name,
- util.guessMimeType(self._content.file_name),
- self._file_stream.read()
- )
- self._api.save(self._content, ActionDescription.REVISION)
|