def update_file_info(self,
                      context,
                      request: TracimRequest,
                      hapic_data=None) -> ContentInContext:  # nopep8
     """
     update thread
     """
     app_config = request.registry.settings['CFG']
     api = ContentApi(
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config,
     )
     content = api.get_one(hapic_data.path.content_id,
                           content_type=ContentType.Any)
     with new_revision(session=request.dbsession,
                       tm=transaction.manager,
                       content=content):
         api.update_content(
             item=content,
             new_label=hapic_data.body.label,
             new_content=hapic_data.body.raw_content,
         )
         api.save(content)
     return api.get_content_in_context(content)
Exemple #2
0
 def create_generic_empty_content(
         self,
         context,
         request: TracimRequest,
         hapic_data=None,
 ) -> ContentInContext:
     """
     create a generic empty content
     """
     app_config = request.registry.settings['CFG']
     creation_data = hapic_data.body
     api = ContentApi(
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config
     )
     parent = None
     if creation_data.parent_id:
         try:
             parent = api.get_one(content_id=creation_data.parent_id, content_type=ContentType.Any)  # nopep8
         except ContentNotFound as exc:
             raise ParentNotFound(
                 'Parent with content_id {} not found'.format(creation_data.parent_id)
             ) from exc
     content = api.create(
         label=creation_data.label,
         content_type=creation_data.content_type,
         workspace=request.current_workspace,
         parent=parent,
     )
     api.save(content, ActionDescription.CREATION)
     content = api.get_content_in_context(content)
     return content
 def set_html_document_status(
         self,
         context,
         request: TracimRequest,
         hapic_data=None
 ) -> None:
     """
     set html_document status
     """
     app_config = request.registry.settings['CFG']
     api = ContentApi(
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config,
     )
     content = api.get_one(
         hapic_data.path.content_id,
         content_type=ContentType.Any
     )
     with new_revision(
             session=request.dbsession,
             tm=transaction.manager,
             content=content
     ):
         api.set_status(
             content,
             hapic_data.body.status,
         )
         api.save(content)
     return
Exemple #4
0
class WorkspaceResource(DAVCollection):
    """
    Workspace resource corresponding to tracim's workspaces.
    Direct children can only be folders, though files might come later on and are supported
    """

    def __init__(self,
                 path: str,
                 environ: dict,
                 workspace: Workspace,
                 user: User,
                 session: Session
    ) -> None:
        super(WorkspaceResource, self).__init__(path, environ)

        self.workspace = workspace
        self.content = None
        self.user = user
        self.session = session
        self.content_api = ContentApi(
            current_user=self.user,
            session=session,
            config=self.provider.app_config,
            show_temporary=True
        )

        self._file_count = 0

    def __repr__(self) -> str:
        return "<DAVCollection: Workspace (%d)>" % self.workspace.workspace_id

    def getPreferredPath(self):
        return self.path

    def getCreationDate(self) -> float:
        return mktime(self.workspace.created.timetuple())

    def getDisplayName(self) -> str:
        return self.workspace.label

    def getLastModified(self) -> float:
        return mktime(self.workspace.updated.timetuple())

    def getMemberNames(self) -> [str]:
        retlist = []

        children = self.content_api.get_all(
            parent_id=self.content.id if self.content is not None else None,
            workspace=self.workspace
        )

        for content in children:
            # the purpose is to display .history only if there's at least one content's type that has a history
            if content.type != ContentType.Folder:
                self._file_count += 1
            retlist.append(content.get_label_as_file())

        return retlist

    def getMember(self, content_label: str) -> _DAVResource:

        return self.provider.getResourceInst(
            '%s/%s' % (self.path, transform_to_display(content_label)),
            self.environ
        )

    def createEmptyResource(self, file_name: str):
        """
        [For now] we don't allow to create files right under workspaces.
        Though if we come to allow it, deleting the error's raise will make it possible.
        """
        # TODO : remove commentary here raise DAVError(HTTP_FORBIDDEN)
        if '/.deleted/' in self.path or '/.archived/' in self.path:
            raise DAVError(HTTP_FORBIDDEN)

        content = None

        # Note: To prevent bugs, check here again if resource already exist
        path = os.path.join(self.path, file_name)
        resource = self.provider.getResourceInst(path, self.environ)
        if resource:
            content = resource.content

        return FakeFileStream(
            session=self.session,
            file_name=file_name,
            content_api=self.content_api,
            workspace=self.workspace,
            content=content,
            parent=self.content,
            path=self.path + '/' + file_name
        )

    def createCollection(self, label: str) -> 'FolderResource':
        """
        Create a new folder for the current workspace. As it's not possible for the user to choose
        which types of content are allowed in this folder, we allow allow all of them.

        This method return the DAVCollection created.
        """

        if '/.deleted/' in self.path or '/.archived/' in self.path:
            raise DAVError(HTTP_FORBIDDEN)

        folder = self.content_api.create(
            content_type=ContentType.Folder,
            workspace=self.workspace,
            label=label,
            parent=self.content
        )

        subcontent = dict(
            folder=True,
            thread=True,
            file=True,
            page=True
        )

        self.content_api.set_allowed_content(folder, subcontent)
        self.content_api.save(folder)

        transaction.commit()

        return FolderResource('%s/%s' % (self.path, transform_to_display(label)),
                              self.environ,
                              content=folder,
                              session=self.session,
                              user=self.user,
                              workspace=self.workspace,
                              )

    def delete(self):
        """For now, it is not possible to delete a workspace through the webdav client."""
        raise DAVError(HTTP_FORBIDDEN)

    def supportRecursiveMove(self, destpath):
        return True

    def moveRecursive(self, destpath):
        if dirname(normpath(destpath)) == self.environ['http_authenticator.realm']:
            self.workspace.label = basename(normpath(destpath))
            transaction.commit()
        else:
            raise DAVError(HTTP_FORBIDDEN)

    def getMemberList(self) -> [_DAVResource]:
        members = []

        children = self.content_api.get_all(False, ContentType.Any, self.workspace)

        for content in children:
            content_path = '%s/%s' % (self.path, transform_to_display(content.get_label_as_file()))

            if content.type == ContentType.Folder:
                members.append(
                    FolderResource(
                        path=content_path,
                        environ=self.environ,
                        workspace=self.workspace,
                        user=self.user,
                        content=content,
                        session=self.session,
                    )
                )
            elif content.type == ContentType.File:
                self._file_count += 1
                members.append(
                    FileResource(
                        path=content_path,
                        environ=self.environ,
                        content=content,
                        user=self.user,
                        session=self.session,
                    )
                )
            else:
                self._file_count += 1
                members.append(
                    OtherFileResource(
                        content_path,
                        self.environ,
                        content,
                        session=self.session,
                        user=self.user,
                    ))

        if self._file_count > 0 and self.provider.show_history():
            members.append(
                HistoryFolderResource(
                    path=self.path + '/' + ".history",
                    environ=self.environ,
                    content=self.content,
                    workspace=self.workspace,
                    type=HistoryType.Standard,
                    session=self.session,
                    user=self.user,
                )
            )

        if self.provider.show_delete():
            members.append(
                DeletedFolderResource(
                    path=self.path + '/' + ".deleted",
                    environ=self.environ,
                    content=self.content,
                    workspace=self.workspace,
                    session=self.session,
                    user=self.user,
                )
            )

        if self.provider.show_archive():
            members.append(
                ArchivedFolderResource(
                    path=self.path + '/' + ".archived",
                    environ=self.environ,
                    content=self.content,
                    workspace=self.workspace,
                    user=self.user,
                    session=self.session,
                )
            )

        return members
Exemple #5
0
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()
Exemple #6
0
    def insert(self):
        admin = self._session.query(models.User) \
            .filter(models.User.email == '*****@*****.**') \
            .one()
        bob = self._session.query(models.User) \
            .filter(models.User.email == '*****@*****.**') \
            .one()
        john_the_reader = self._session.query(models.User) \
            .filter(models.User.email == '*****@*****.**') \
            .one()

        admin_workspace_api = WorkspaceApi(
            current_user=admin,
            session=self._session,
            config=self._config,
        )
        bob_workspace_api = WorkspaceApi(current_user=bob,
                                         session=self._session,
                                         config=self._config)
        content_api = ContentApi(current_user=admin,
                                 session=self._session,
                                 config=self._config)
        bob_content_api = ContentApi(current_user=bob,
                                     session=self._session,
                                     config=self._config)
        reader_content_api = ContentApi(current_user=john_the_reader,
                                        session=self._session,
                                        config=self._config)
        role_api = RoleApi(
            current_user=admin,
            session=self._session,
            config=self._config,
        )

        # Workspaces
        business_workspace = admin_workspace_api.create_workspace(
            'Business',
            description='All importants documents',
            save_now=True,
        )
        recipe_workspace = admin_workspace_api.create_workspace(
            'Recipes',
            description='Our best recipes',
            save_now=True,
        )
        other_workspace = bob_workspace_api.create_workspace(
            'Others',
            description='Other Workspace',
            save_now=True,
        )

        # Workspaces roles
        role_api.create_one(
            user=bob,
            workspace=recipe_workspace,
            role_level=UserRoleInWorkspace.CONTENT_MANAGER,
            with_notif=False,
        )
        role_api.create_one(
            user=john_the_reader,
            workspace=recipe_workspace,
            role_level=UserRoleInWorkspace.READER,
            with_notif=False,
        )
        # Folders

        tool_workspace = content_api.create(
            content_type=ContentType.Folder,
            workspace=business_workspace,
            label='Tools',
            do_save=True,
            do_notify=False,
        )
        menu_workspace = content_api.create(
            content_type=ContentType.Folder,
            workspace=business_workspace,
            label='Menus',
            do_save=True,
            do_notify=False,
        )

        dessert_folder = content_api.create(
            content_type=ContentType.Folder,
            workspace=recipe_workspace,
            label='Desserts',
            do_save=True,
            do_notify=False,
        )
        salads_folder = content_api.create(
            content_type=ContentType.Folder,
            workspace=recipe_workspace,
            label='Salads',
            do_save=True,
            do_notify=False,
        )
        other_folder = content_api.create(
            content_type=ContentType.Folder,
            workspace=other_workspace,
            label='Infos',
            do_save=True,
            do_notify=False,
        )

        # Pages, threads, ..
        tiramisu_page = content_api.create(
            content_type=ContentType.Page,
            workspace=recipe_workspace,
            parent=dessert_folder,
            label='Tiramisu Recipes!!!',
            do_save=True,
            do_notify=False,
        )
        with new_revision(
                session=self._session,
                tm=transaction.manager,
                content=tiramisu_page,
        ):
            content_api.update_content(
                item=tiramisu_page,
                new_content=
                '<p>To cook a greet Tiramisu, you need many ingredients.</p>',  # nopep8
                new_label='Tiramisu Recipes!!!',
            )
            content_api.save(tiramisu_page)

        best_cake_thread = content_api.create(
            content_type=ContentType.Thread,
            workspace=recipe_workspace,
            parent=dessert_folder,
            label='Best Cake',
            do_save=False,
            do_notify=False,
        )
        best_cake_thread.description = 'Which is the best cake?'
        self._session.add(best_cake_thread)
        apple_pie_recipe = content_api.create(
            content_type=ContentType.File,
            workspace=recipe_workspace,
            parent=dessert_folder,
            label='Apple_Pie',
            do_save=False,
            do_notify=False,
        )
        apple_pie_recipe.file_extension = '.txt'
        apple_pie_recipe.depot_file = FileIntent(
            b'Apple pie Recipe',
            'apple_Pie.txt',
            'text/plain',
        )
        self._session.add(apple_pie_recipe)
        Brownie_recipe = content_api.create(
            content_type=ContentType.File,
            workspace=recipe_workspace,
            parent=dessert_folder,
            label='Brownie Recipe',
            do_save=False,
            do_notify=False,
        )
        Brownie_recipe.file_extension = '.html'
        Brownie_recipe.depot_file = FileIntent(
            b'<p>Brownie Recipe</p>',
            'brownie_recipe.html',
            'text/html',
        )
        self._session.add(Brownie_recipe)
        fruits_desserts_folder = content_api.create(
            content_type=ContentType.Folder,
            workspace=recipe_workspace,
            label='Fruits Desserts',
            parent=dessert_folder,
            do_save=True,
        )

        menu_page = content_api.create(
            content_type=ContentType.Page,
            workspace=business_workspace,
            parent=menu_workspace,
            label='Current Menu',
            do_save=True,
        )

        new_fruit_salad = content_api.create(
            content_type=ContentType.Page,
            workspace=recipe_workspace,
            parent=fruits_desserts_folder,
            label='New Fruit Salad',
            do_save=True,
        )
        old_fruit_salad = content_api.create(
            content_type=ContentType.Page,
            workspace=recipe_workspace,
            parent=fruits_desserts_folder,
            label='Fruit Salad',
            do_save=True,
            do_notify=False,
        )
        with new_revision(
                session=self._session,
                tm=transaction.manager,
                content=old_fruit_salad,
        ):
            content_api.archive(old_fruit_salad)
        content_api.save(old_fruit_salad)

        bad_fruit_salad = content_api.create(
            content_type=ContentType.Page,
            workspace=recipe_workspace,
            parent=fruits_desserts_folder,
            label='Bad Fruit Salad',
            do_save=True,
            do_notify=False,
        )
        with new_revision(
                session=self._session,
                tm=transaction.manager,
                content=bad_fruit_salad,
        ):
            content_api.delete(bad_fruit_salad)
        content_api.save(bad_fruit_salad)

        # File at the root for test
        new_fruit_salad = content_api.create(
            content_type=ContentType.Page,
            workspace=other_workspace,
            label='New Fruit Salad',
            do_save=True,
        )
        old_fruit_salad = content_api.create(
            content_type=ContentType.Page,
            workspace=other_workspace,
            label='Fruit Salad',
            do_save=True,
        )
        with new_revision(
                session=self._session,
                tm=transaction.manager,
                content=old_fruit_salad,
        ):
            content_api.archive(old_fruit_salad)
        content_api.save(old_fruit_salad)

        bad_fruit_salad = content_api.create(
            content_type=ContentType.Page,
            workspace=other_workspace,
            label='Bad Fruit Salad',
            do_save=True,
        )
        with new_revision(
                session=self._session,
                tm=transaction.manager,
                content=bad_fruit_salad,
        ):
            content_api.delete(bad_fruit_salad)
        content_api.save(bad_fruit_salad)

        content_api.create_comment(
            parent=best_cake_thread,
            content=
            '<p>What is for you the best cake ever? </br> I personnally vote for Chocolate cupcake!</p>',  # nopep8
            do_save=True,
        )
        bob_content_api.create_comment(
            parent=best_cake_thread,
            content='<p>What about Apple Pie? There are Awesome!</p>',
            do_save=True,
        )
        reader_content_api.create_comment(
            parent=best_cake_thread,
            content=
            '<p>You are right, but Kouign-amann are clearly better.</p>',
            do_save=True,
        )
        with new_revision(
                session=self._session,
                tm=transaction.manager,
                content=best_cake_thread,
        ):
            bob_content_api.update_content(
                item=best_cake_thread,
                new_content='What is the best cake?',
                new_label='Best Cakes?',
            )
            bob_content_api.save(best_cake_thread)

        with new_revision(
                session=self._session,
                tm=transaction.manager,
                content=tiramisu_page,
        ):
            bob_content_api.update_content(
                item=tiramisu_page,
                new_content=
                '<p>To cook a great Tiramisu, you need many ingredients.</p>',  # nopep8
                new_label='Tiramisu Recipe',
            )
            bob_content_api.save(tiramisu_page)
        self._session.flush()