コード例 #1
0
    def test_api___simple_search_ok__no_search_string(self) -> None:
        dbsession = get_tm_session(self.session_factory, transaction.manager)
        admin = dbsession.query(User).filter(User.email == "*****@*****.**").one()
        uapi = UserApi(current_user=admin, session=dbsession, config=self.app_config)
        gapi = GroupApi(current_user=admin, session=dbsession, config=self.app_config)
        groups = [gapi.get_one_with_name("trusted-users")]
        user = uapi.create_user(
            "*****@*****.**",
            password="******",
            do_save=True,
            do_notify=False,
            groups=groups,
        )
        workspace_api = WorkspaceApi(
            current_user=admin, session=dbsession, config=self.app_config, show_deleted=True
        )
        workspace = workspace_api.create_workspace("test", save_now=True)
        rapi = RoleApi(current_user=admin, session=dbsession, config=self.app_config)
        rapi.create_one(user, workspace, UserRoleInWorkspace.WORKSPACE_MANAGER, False)
        api = ContentApi(session=dbsession, current_user=user, config=self.app_config)
        api.create(
            content_type_slug="html-document", workspace=workspace, label="test", do_save=True
        )
        transaction.commit()

        self.testapp.authorization = ("Basic", ("*****@*****.**", "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(), status=200)
        search_result = res.json_body
        assert search_result
        assert search_result["total_hits"] == 0
        assert search_result["is_total_hits_accurate"] is True
        assert len(search_result["contents"]) == 0
コード例 #2
0
    def test_func__create_new_content_with_notification__ok__nominal_case(self):
        uapi = UserApi(current_user=None, session=self.session, config=self.app_config)
        current_user = uapi.get_one_by_email("*****@*****.**")
        # Create new user with notification enabled on w1 workspace
        wapi = WorkspaceApi(current_user=current_user, session=self.session, config=self.app_config)
        workspace = wapi.get_one_by_label("Recipes")
        user = uapi.get_one_by_email("*****@*****.**")
        wapi.enable_notifications(user, workspace)

        api = ContentApi(current_user=user, session=self.session, config=self.app_config)
        item = api.create(
            content_type_list.Folder.slug, workspace, None, "parent", do_save=True, do_notify=False
        )
        api.create(
            content_type_list.File.slug, workspace, item, "file1", do_save=True, do_notify=True
        )
        # Send mail async from redis queue with daemon
        daemon = MailSenderDaemon(self.app_config, burst=True)
        daemon.run()
        # check mail received
        response = self.get_mailhog_mails()
        headers = response[0]["Content"]["Headers"]
        assert headers["From"][0] == '"Bob i. via Tracim" <test_user_from+3@localhost>'
        assert headers["To"][0] == "Global manager <*****@*****.**>"
        assert headers["Subject"][0] == "[TRACIM] [Recipes] file1 (Open)"
        assert headers["References"][0] == "test_user_refs+22@localhost"
        assert (
            headers["Reply-to"][0]
            == '"Bob i. & all members of Recipes" <test_user_reply+22@localhost>'
        )
コード例 #3
0
    def test_api___simple_search_ok__by_comment_content(
        self,
        created_content_name,
        search_string,
        nb_content_result,
        first_search_result_content_name,
        first_created_comment_content,
        second_created_comment_content,
    ) -> None:
        dbsession = get_tm_session(self.session_factory, transaction.manager)
        admin = dbsession.query(User).filter(User.email == "*****@*****.**").one()
        uapi = UserApi(current_user=admin, session=dbsession, config=self.app_config)
        gapi = GroupApi(current_user=admin, session=dbsession, config=self.app_config)
        groups = [gapi.get_one_with_name("trusted-users")]
        user = uapi.create_user(
            "*****@*****.**",
            password="******",
            do_save=True,
            do_notify=False,
            groups=groups,
        )
        workspace_api = WorkspaceApi(
            current_user=admin, session=dbsession, config=self.app_config, show_deleted=True
        )
        workspace = workspace_api.create_workspace("test", save_now=True)
        rapi = RoleApi(current_user=admin, session=dbsession, config=self.app_config)
        rapi.create_one(user, workspace, UserRoleInWorkspace.WORKSPACE_MANAGER, False)
        api = ContentApi(session=dbsession, current_user=user, config=self.app_config)
        content = api.create(
            content_type_slug="html-document",
            workspace=workspace,
            label=created_content_name,
            do_save=True,
        )
        api.create_comment(
            workspace=workspace, parent=content, content=first_created_comment_content, do_save=True
        )
        api.create_comment(
            workspace=workspace,
            parent=content,
            content=second_created_comment_content,
            do_save=True,
        )
        api.create(
            content_type_slug="html-document", workspace=workspace, label="report", do_save=True
        )
        api.create(
            content_type_slug="thread", workspace=workspace, label="discussion", do_save=True
        )
        transaction.commit()

        self.testapp.authorization = ("Basic", ("*****@*****.**", "*****@*****.**"))
        params = {"search_string": search_string}
        res = self.testapp.get("/api/v2/search/content".format(), status=200, params=params)
        search_result = res.json_body
        assert search_result
        assert search_result["total_hits"] == nb_content_result
        assert search_result["is_total_hits_accurate"] is False
        assert search_result["contents"][0]["label"] == first_search_result_content_name
コード例 #4
0
    def test_func__create_new_content_with_notification__ok__nominal_case(
            self):
        uapi = UserApi(
            current_user=None,
            session=self.session,
            config=self.app_config,
        )
        current_user = uapi.get_one_by_email('*****@*****.**')
        # Create new user with notification enabled on w1 workspace
        wapi = WorkspaceApi(
            current_user=current_user,
            session=self.session,
            config=self.app_config,
        )
        workspace = wapi.get_one_by_label('Recipes')
        user = uapi.get_one_by_email('*****@*****.**')
        wapi.enable_notifications(user, workspace)

        api = ContentApi(
            current_user=user,
            session=self.session,
            config=self.app_config,
        )
        item = api.create(
            content_type_list.Folder.slug,
            workspace,
            None,
            'parent',
            do_save=True,
            do_notify=False,
        )
        item2 = api.create(
            content_type_list.File.slug,
            workspace,
            item,
            'file1',
            do_save=True,
            do_notify=True,
        )
        # Send mail async from redis queue
        redis = get_redis_connection(self.app_config)
        queue = get_rq_queue(
            redis,
            'mail_sender',
        )
        worker = SimpleWorker([queue], connection=queue.connection)
        worker.work(burst=True)
        # check mail received
        response = requests.get('http://127.0.0.1:8025/api/v1/messages')
        response = response.json()
        headers = response[0]['Content']['Headers']
        assert headers['From'][
            0] == '"Bob i. via Tracim" <test_user_from+3@localhost>'  # nopep8
        assert headers['To'][0] == 'Global manager <*****@*****.**>'
        assert headers['Subject'][0] == '[TRACIM] [Recipes] file1 (Open)'
        assert headers['References'][0] == 'test_user_refs+22@localhost'
        assert headers['Reply-to'][
            0] == '"Bob i. & all members of Recipes" <test_user_reply+22@localhost>'  # nopep8
コード例 #5
0
    def test_func__create_comment_with_notification__ok__nominal_case(self):
        uapi = UserApi(
            current_user=None,
            session=self.session,
            config=self.app_config,
        )
        current_user = uapi.get_one_by_email('*****@*****.**')
        # set admin as french, useful to verify if i18n work properly
        current_user.lang = 'fr'
        # Create new user with notification enabled on w1 workspace
        wapi = WorkspaceApi(
            current_user=current_user,
            session=self.session,
            config=self.app_config,
        )
        workspace = wapi.get_one_by_label('Recipes')
        user = uapi.get_one_by_email('*****@*****.**')
        wapi.enable_notifications(user, workspace)

        api = ContentApi(
            current_user=user,
            session=self.session,
            config=self.app_config,
        )
        item = api.create(
            content_type_list.Folder.slug,
            workspace,
            None,
            'parent',
            do_save=True,
            do_notify=False,
        )
        item2 = api.create(
            content_type_list.File.slug,
            workspace,
            item,
            'file1',
            do_save=True,
            do_notify=False,
        )
        api.create_comment(parent=item2,
                           content='My super comment',
                           do_save=True,
                           do_notify=True)
        transaction.commit()

        # check mail received
        response = requests.get('http://127.0.0.1:8025/api/v1/messages')
        response = response.json()
        headers = response[0]['Content']['Headers']
        assert headers['From'][
            0] == '"Bob i. via Tracim" <test_user_from+3@localhost>'  # nopep8
        assert headers['To'][0] == 'Global manager <*****@*****.**>'
        assert headers['Subject'][0] == '[TRACIM] [Recipes] file1 (Open)'
        assert headers['References'][0] == 'test_user_refs+22@localhost'
        assert headers['Reply-to'][
            0] == '"Bob i. & all members of Recipes" <test_user_reply+22@localhost>'  # nopep8
コード例 #6
0
    def test_func__create_new_content_with_notification__ok__nominal_case(self):
        uapi = UserApi(
            current_user=None,
            session=self.session,
            config=self.app_config,
        )
        current_user = uapi.get_one_by_email('*****@*****.**')
        # Create new user with notification enabled on w1 workspace
        wapi = WorkspaceApi(
            current_user=current_user,
            session=self.session,
            config=self.app_config,
        )
        workspace = wapi.get_one_by_label('Recipes')
        user = uapi.get_one_by_email('*****@*****.**')
        wapi.enable_notifications(user, workspace)

        api = ContentApi(
            current_user=user,
            session=self.session,
            config=self.app_config,
        )
        item = api.create(
            content_type_list.Folder.slug,
            workspace,
            None,
            'parent',
            do_save=True,
            do_notify=False,
        )
        item2 = api.create(
            content_type_list.File.slug,
            workspace,
            item,
            'file1',
            do_save=True,
            do_notify=True,
        )
        # Send mail async from redis queue
        redis = get_redis_connection(
            self.app_config
        )
        queue = get_rq_queue(
            redis,
            'mail_sender',
        )
        worker = SimpleWorker([queue], connection=queue.connection)
        worker.work(burst=True)
        # check mail received
        response = self.get_mailhog_mails()
        headers = response[0]['Content']['Headers']
        assert headers['From'][0] == '"Bob i. via Tracim" <test_user_from+3@localhost>'  # nopep8
        assert headers['To'][0] == 'Global manager <*****@*****.**>'
        assert headers['Subject'][0] == '[TRACIM] [Recipes] file1 (Open)'
        assert headers['References'][0] == 'test_user_refs+22@localhost'
        assert headers['Reply-to'][0] == '"Bob i. & all members of Recipes" <test_user_reply+22@localhost>'  # nopep8
コード例 #7
0
    def test_func__create_new_content_with_notification__ok__nominal_case(
            self):
        uapi = UserApi(
            current_user=None,
            session=self.session,
            config=self.app_config,
        )
        current_user = uapi.get_one_by_email('*****@*****.**')
        # Create new user with notification enabled on w1 workspace
        wapi = WorkspaceApi(
            current_user=current_user,
            session=self.session,
            config=self.app_config,
        )
        workspace = wapi.get_one_by_label('Recipes')
        user = uapi.get_one_by_email('*****@*****.**')
        wapi.enable_notifications(user, workspace)

        api = ContentApi(
            current_user=user,
            session=self.session,
            config=self.app_config,
        )
        item = api.create(
            content_type_list.Folder.slug,
            workspace,
            None,
            'parent',
            do_save=True,
            do_notify=False,
        )
        item2 = api.create(
            content_type_list.File.slug,
            workspace,
            item,
            'file1',
            do_save=True,
            do_notify=True,
        )
        # Send mail async from redis queue with daemon
        daemon = MailSenderDaemon(self.app_config, burst=True)
        daemon.run()
        # check mail received
        response = self.get_mailhog_mails()
        headers = response[0]['Content']['Headers']
        assert headers['From'][
            0] == '"Bob i. via Tracim" <test_user_from+3@localhost>'  # nopep8
        assert headers['To'][0] == 'Global manager <*****@*****.**>'
        assert headers['Subject'][0] == '[TRACIM] [Recipes] file1 (Open)'
        assert headers['References'][0] == 'test_user_refs+22@localhost'
        assert headers['Reply-to'][
            0] == '"Bob i. & all members of Recipes" <test_user_reply+22@localhost>'  # nopep8
コード例 #8
0
    def test_func__create_comment_with_notification__ok__nominal_case(self):
        uapi = UserApi(
            current_user=None,
            session=self.session,
            config=self.app_config,
        )
        current_user = uapi.get_one_by_email('*****@*****.**')
        # set admin as french, useful to verify if i18n work properly
        current_user.lang = 'fr'
        # Create new user with notification enabled on w1 workspace
        wapi = WorkspaceApi(
            current_user=current_user,
            session=self.session,
            config=self.app_config,
        )
        workspace = wapi.get_one_by_label('Recipes')
        user = uapi.get_one_by_email('*****@*****.**')
        wapi.enable_notifications(user, workspace)

        api = ContentApi(
            current_user=user,
            session=self.session,
            config=self.app_config,
        )
        item = api.create(
            content_type_list.Folder.slug,
            workspace,
            None,
            'parent',
            do_save=True,
            do_notify=False,
        )
        item2 = api.create(
            content_type_list.File.slug,
            workspace,
            item,
            'file1',
            do_save=True,
            do_notify=False,
        )
        api.create_comment(parent=item2, content='My super comment', do_save=True, do_notify=True)
        transaction.commit()

        # check mail received
        response = self.get_mailhog_mails()
        headers = response[0]['Content']['Headers']
        assert headers['From'][0] == '"Bob i. via Tracim" <test_user_from+3@localhost>'  # nopep8
        assert headers['To'][0] == 'Global manager <*****@*****.**>'
        assert headers['Subject'][0] == '[TRACIM] [Recipes] file1 (Open)'
        assert headers['References'][0] == 'test_user_refs+22@localhost'
        assert headers['Reply-to'][0] == '"Bob i. & all members of Recipes" <test_user_reply+22@localhost>'  # nopep8
コード例 #9
0
 def create_generic_empty_content(self,
                                  context,
                                  request: TracimRequest,
                                  hapic_data=None) -> ContentInContext:
     """
     Creates a generic empty content. The minimum viable content has a label and a content type.
     Creating a content generally starts with a request to this endpoint.
     For specific contents like files, it is recommended to use the dedicated endpoint.
     This feature is accessible to contributors and higher role only.
     """
     app_config = request.registry.settings["CFG"]  # type: 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=content_type_list.Any_SLUG)
         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_slug=creation_data.content_type,
         workspace=request.current_workspace,
         parent=parent,
     )
     api.save(content, ActionDescription.CREATION)
     api.execute_created_content_actions(content)
     content = api.get_content_in_context(content)
     return content
コード例 #10
0
    def test_api__elasticsearch_search__ok__in_file_ingest_search(self):
        dbsession = get_tm_session(self.session_factory, transaction.manager)
        admin = dbsession.query(User).filter(
            User.email == "*****@*****.**").one()
        uapi = UserApi(current_user=admin,
                       session=dbsession,
                       config=self.app_config)
        gapi = GroupApi(current_user=admin,
                        session=dbsession,
                        config=self.app_config)
        groups = [gapi.get_one_with_name("trusted-users")]
        user = uapi.create_user(
            "*****@*****.**",
            password="******",
            do_save=True,
            do_notify=False,
            groups=groups,
        )
        workspace_api = WorkspaceApi(current_user=admin,
                                     session=dbsession,
                                     config=self.app_config,
                                     show_deleted=True)
        workspace = workspace_api.create_workspace("test", save_now=True)
        rapi = RoleApi(current_user=admin,
                       session=dbsession,
                       config=self.app_config)
        rapi.create_one(user, workspace, UserRoleInWorkspace.WORKSPACE_MANAGER,
                        False)
        api = ContentApi(session=dbsession,
                         current_user=user,
                         config=self.app_config)
        with self.session.no_autoflush:
            text_file = api.create(
                content_type_slug=content_type_list.File.slug,
                workspace=workspace,
                label="important",
                do_save=False,
            )
            api.update_file_data(text_file, "test_file", "text/plain",
                                 b"we need to find stringtosearch here !")
            api.save(text_file)
            api.execute_created_content_actions(text_file)
        content_id = text_file.content_id
        transaction.commit()
        self.refresh_elasticsearch()

        params = {"search_string": "stringtosearch"}
        self.testapp.authorization = ("Basic", ("*****@*****.**",
                                                "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(),
                               status=200,
                               params=params)
        search_result = res.json_body
        assert search_result
        assert search_result["total_hits"] == 1
        assert search_result["is_total_hits_accurate"] is True
        assert len(search_result["contents"]) == 1
        assert search_result["contents"][0]["content_id"] == content_id
コード例 #11
0
ファイル: test_comments.py プロジェクト: tracim/tracim_v2
 def test_api__post_content_comment__ok_200__nominal_case(self) -> None:
     """
     Get alls comments of a content
     """
     dbsession = get_tm_session(self.session_factory, transaction.manager)
     admin = dbsession.query(models.User) \
         .filter(models.User.email == '*****@*****.**') \
         .one() # type: models.User
     workspace_api = WorkspaceApi(current_user=admin,
                                  session=dbsession,
                                  config=self.app_config)
     business_workspace = workspace_api.get_one(1)
     content_api = ContentApi(current_user=admin,
                              session=dbsession,
                              config=self.app_config)
     tool_folder = content_api.get_one(
         1, content_type=content_type_list.Any_SLUG)
     test_thread = content_api.create(
         content_type_slug=content_type_list.Thread.slug,
         workspace=business_workspace,
         parent=tool_folder,
         label='Test Thread',
         do_save=True,
         do_notify=False,
     )
     with new_revision(
             session=dbsession,
             tm=transaction.manager,
             content=test_thread,
     ):
         content_api.update_content(test_thread,
                                    new_label='test_thread_updated',
                                    new_content='Just a test')
     transaction.commit()
     self.testapp.authorization = ('Basic', ('*****@*****.**',
                                             '*****@*****.**'))
     params = {'raw_content': 'I strongly disagree, Tiramisu win!'}
     res = self.testapp.post_json(
         '/api/v2/workspaces/{}/contents/{}/comments'.format(
             business_workspace.workspace_id, test_thread.content_id),
         params=params,
         status=200)
     comment = res.json_body
     assert comment['content_id']
     assert comment['parent_id'] == test_thread.content_id
     assert comment['raw_content'] == 'I strongly disagree, Tiramisu win!'
     assert comment['author']
     assert comment['author']['user_id'] == admin.user_id
     # TODO - G.M - 2018-06-172 - [avatar] setup avatar url
     assert comment['author']['avatar_url'] is None
     assert comment['author']['public_name'] == admin.display_name
     # TODO - G.M - 2018-06-179 - better check for datetime
     assert comment['created']
コード例 #12
0
 def test_api__post_content_comment__ok_200__nominal_case(self) -> None:
     """
     Get alls comments of a content
     """
     dbsession = get_tm_session(self.session_factory, transaction.manager)
     admin = dbsession.query(User).filter(
         User.email == "*****@*****.**").one()  # type: User
     workspace_api = WorkspaceApi(current_user=admin,
                                  session=dbsession,
                                  config=self.app_config)
     business_workspace = workspace_api.get_one(1)
     content_api = ContentApi(current_user=admin,
                              session=dbsession,
                              config=self.app_config)
     tool_folder = content_api.get_one(
         1, content_type=content_type_list.Any_SLUG)
     test_thread = content_api.create(
         content_type_slug=content_type_list.Thread.slug,
         workspace=business_workspace,
         parent=tool_folder,
         label="Test Thread",
         do_save=True,
         do_notify=False,
     )
     with new_revision(session=dbsession,
                       tm=transaction.manager,
                       content=test_thread):
         content_api.update_content(test_thread,
                                    new_label="test_thread_updated",
                                    new_content="Just a test")
     transaction.commit()
     self.testapp.authorization = ("Basic", ("*****@*****.**",
                                             "*****@*****.**"))
     params = {"raw_content": "I strongly disagree, Tiramisu win!"}
     res = self.testapp.post_json(
         "/api/v2/workspaces/{}/contents/{}/comments".format(
             business_workspace.workspace_id, test_thread.content_id),
         params=params,
         status=200,
     )
     comment = res.json_body
     assert comment["content_id"]
     assert comment["parent_id"] == test_thread.content_id
     assert comment["raw_content"] == "I strongly disagree, Tiramisu win!"
     assert comment["author"]
     assert comment["author"]["user_id"] == admin.user_id
     # TODO - G.M - 2018-06-172 - [avatar] setup avatar url
     assert comment["author"]["avatar_url"] is None
     assert comment["author"]["public_name"] == admin.display_name
     # TODO - G.M - 2018-06-179 - better check for datetime
     assert comment["created"]
コード例 #13
0
ファイル: controller.py プロジェクト: grignards/tracim
 def create_file(self, context, request: TracimRequest, hapic_data=None):
     """
     Create a file .This will create 2 new revision.
     """
     # INFO - G.M - 2019-09-03 - check validation of file here, because marshmallow
     # required doesn't work correctly with cgi_fieldstorage.
     # check is done with None because cgi_fieldstorage cannot be converted to bool
     if hapic_data.files.files is None:
         raise NoFileValidationError(
             'No file "files" given at input, validation failed.')
     app_config = request.registry.settings["CFG"]  # type: CFG
     api = ContentApi(
         show_archived=True,
         show_deleted=True,
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config,
     )
     api.check_upload_size(request.content_length,
                           request.current_workspace)
     _file = hapic_data.files.files
     parent_id = hapic_data.forms.parent_id
     parent = None  # type: typing.Optional['Content']
     if parent_id:
         try:
             parent = api.get_one(content_id=parent_id,
                                  content_type=content_type_list.Any_SLUG)
         except ContentNotFound as exc:
             raise ParentNotFound(
                 "Parent with content_id {} not found".format(
                     parent_id)) from exc
     content = api.create(
         filename=_file.filename,
         content_type_slug=FILE_TYPE,
         workspace=request.current_workspace,
         parent=parent,
     )
     api.save(content, ActionDescription.CREATION)
     with new_revision(session=request.dbsession,
                       tm=transaction.manager,
                       content=content):
         api.update_file_data(
             content,
             new_filename=_file.filename,
             new_mimetype=_file.type,
             new_content=_file.file,
         )
     api.execute_created_content_actions(content)
     return api.get_content_in_context(content)
コード例 #14
0
ファイル: file_controller.py プロジェクト: tracim/tracim
    def create_file(self, context, request: TracimRequest, hapic_data=None):
        """
        Create a file .This will create 2 new revision.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        _file = hapic_data.files.files
        parent_id = hapic_data.forms.parent_id
        api = ContentApi(
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config
        )

        parent = None  # type: typing.Optional['Content']
        if parent_id:
            try:
                parent = api.get_one(content_id=parent_id, content_type=content_type_list.Any_SLUG)  # nopep8
            except ContentNotFound as exc:
                raise ParentNotFound(
                    'Parent with content_id {} not found'.format(parent_id)
                ) from exc
        content = api.create(
            filename=_file.filename,
            content_type_slug=FILE_TYPE,
            workspace=request.current_workspace,
            parent=parent,
        )
        api.save(content, ActionDescription.CREATION)
        with new_revision(
                session=request.dbsession,
                tm=transaction.manager,
                content=content
        ):
            api.update_file_data(
                content,
                new_filename=_file.filename,
                new_mimetype=_file.type,
                new_content=_file.file,
            )

        return api.get_content_in_context(content)
コード例 #15
0
ファイル: file_controller.py プロジェクト: tracim/tracim_v2
    def create_file(self, context, request: TracimRequest, hapic_data=None):
        """
        Create a file .This will create 2 new revision.
        """
        app_config = request.registry.settings['CFG']
        api = ContentApi(
            show_archived=True,
            show_deleted=True,
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config,
        )
        _file = hapic_data.files.files
        parent_id = hapic_data.forms.parent_id
        api = ContentApi(
            current_user=request.current_user,
            session=request.dbsession,
            config=app_config
        )

        parent = None  # type: typing.Optional['Content']
        if parent_id:
            try:
                parent = api.get_one(content_id=parent_id, content_type=content_type_list.Any_SLUG)  # nopep8
            except ContentNotFound as exc:
                raise ParentNotFound(
                    'Parent with content_id {} not found'.format(parent_id)
                ) from exc
        content = api.create(
            filename=_file.filename,
            content_type_slug=FILE_TYPE,
            workspace=request.current_workspace,
            parent=parent,
        )
        api.save(content, ActionDescription.CREATION)
        with new_revision(
                session=request.dbsession,
                tm=transaction.manager,
                content=content
        ):
            api.update_file_data(
                content,
                new_filename=_file.filename,
                new_mimetype=_file.type,
                new_content=_file.file,
            )

        return api.get_content_in_context(content)
コード例 #16
0
ファイル: test_comments.py プロジェクト: tracim/tracim_v2
 def test_api__post_content_comment__err_400__content_not_editable(
         self) -> None:
     """
     Get alls comments of a content
     """
     dbsession = get_tm_session(self.session_factory, transaction.manager)
     admin = dbsession.query(models.User) \
         .filter(models.User.email == '*****@*****.**') \
         .one() # type: models.User
     workspace_api = WorkspaceApi(current_user=admin,
                                  session=dbsession,
                                  config=self.app_config)
     business_workspace = workspace_api.get_one(1)
     content_api = ContentApi(current_user=admin,
                              session=dbsession,
                              config=self.app_config)
     tool_folder = content_api.get_one(
         1, content_type=content_type_list.Any_SLUG)
     test_thread = content_api.create(
         content_type_slug=content_type_list.Thread.slug,
         workspace=business_workspace,
         parent=tool_folder,
         label='Test Thread',
         do_save=True,
         do_notify=False,
     )
     with new_revision(
             session=dbsession,
             tm=transaction.manager,
             content=test_thread,
     ):
         content_api.update_content(test_thread,
                                    new_label='test_thread_updated',
                                    new_content='Just a test')
     content_api.set_status(test_thread, 'closed-deprecated')
     transaction.commit()
     self.testapp.authorization = ('Basic', ('*****@*****.**',
                                             '*****@*****.**'))
     params = {'raw_content': 'I strongly disagree, Tiramisu win!'}
     res = self.testapp.post_json(
         '/api/v2/workspaces/{}/contents/{}/comments'.format(
             business_workspace.workspace_id, test_thread.content_id),
         params=params,
         status=400)
     assert res.json_body
     assert 'code' in res.json_body
     assert res.json_body['code'] == error.CONTENT_IN_NOT_EDITABLE_STATE
コード例 #17
0
 def test_api__post_content_comment__err_400__content_not_editable(
         self) -> None:
     """
     Get alls comments of a content
     """
     dbsession = get_tm_session(self.session_factory, transaction.manager)
     admin = dbsession.query(User).filter(
         User.email == "*****@*****.**").one()  # type: User
     workspace_api = WorkspaceApi(current_user=admin,
                                  session=dbsession,
                                  config=self.app_config)
     business_workspace = workspace_api.get_one(1)
     content_api = ContentApi(current_user=admin,
                              session=dbsession,
                              config=self.app_config)
     tool_folder = content_api.get_one(
         1, content_type=content_type_list.Any_SLUG)
     test_thread = content_api.create(
         content_type_slug=content_type_list.Thread.slug,
         workspace=business_workspace,
         parent=tool_folder,
         label="Test Thread",
         do_save=True,
         do_notify=False,
     )
     with new_revision(session=dbsession,
                       tm=transaction.manager,
                       content=test_thread):
         content_api.update_content(test_thread,
                                    new_label="test_thread_updated",
                                    new_content="Just a test")
     content_api.set_status(test_thread, "closed-deprecated")
     transaction.commit()
     self.testapp.authorization = ("Basic", ("*****@*****.**",
                                             "*****@*****.**"))
     params = {"raw_content": "I strongly disagree, Tiramisu win!"}
     res = self.testapp.post_json(
         "/api/v2/workspaces/{}/contents/{}/comments".format(
             business_workspace.workspace_id, test_thread.content_id),
         params=params,
         status=400,
     )
     assert res.json_body
     assert "code" in res.json_body
     assert res.json_body["code"] == ErrorCode.CONTENT_IN_NOT_EDITABLE_STATE
コード例 #18
0
ファイル: test_mention.py プロジェクト: inkhey/tracim
def create_content(
    description: str,
    user_api_factory,
    workspace_api_factory,
    session,
    app_config,
    content_type: str = "html-document",
    parent_content: typing.Optional[Content] = None,
) -> Content:
    with transaction.manager:
        uapi = user_api_factory.get()
        try:
            user = uapi.get_one_by_email(email="this.is@user")
        except Exception:
            user = uapi.create_minimal_user(email="this.is@user",
                                            profile=Profile.ADMIN,
                                            save_now=True,
                                            username="******")
        if parent_content:
            workspace = parent_content.workspace
        else:
            workspace = workspace_api_factory.get(user).create_workspace(
                "test workspace", save_now=True)
        api = ContentApi(current_user=user, session=session, config=app_config)
        content = api.create(
            content_type_slug=content_type,
            workspace=workspace,
            parent=parent_content,
            label="Content",
            do_save=True,
        )

        with new_revision(session=session,
                          tm=transaction.manager,
                          content=content):
            content.description = description
            api.update_content(content, new_label=content.label)
            api.save(content)
    return content
コード例 #19
0
 def create_file_from_template(
         self,
         context: DefaultRootFactory,
         request: TracimRequest,
         hapic_data: HapicData = None) -> ContentInContext:
     app_config = request.registry.settings["CFG"]  # type: CFG
     api = ContentApi(current_user=request.current_user,
                      session=request.dbsession,
                      config=app_config)
     collaborative_document_edition_api = CollaborativeDocumentEditionFactory(
     ).get_lib(current_user=request.current_user,
               session=request.dbsession,
               config=app_config)
     collaborative_document_edition_api.check_template_available(
         hapic_data.body.template)
     parent = None  # type: typing.Optional['Content']
     if hapic_data.body.parent_id:
         try:
             parent = api.get_one(content_id=hapic_data.body.parent_id,
                                  content_type=content_type_list.Any_SLUG)
         except ContentNotFound as exc:
             raise ParentNotFound(
                 "Parent with content_id {} not found".format(
                     hapic_data.body.parent_id)) from exc
     content = api.create(
         filename=hapic_data.body.filename,
         content_type_slug=FILE_TYPE,
         workspace=request.current_workspace,
         parent=parent,
     )
     api.save(content, ActionDescription.CREATION)
     with new_revision(session=request.dbsession,
                       tm=transaction.manager,
                       content=content):
         collaborative_document_edition_api.update_content_from_template(
             content=content, template_filename=hapic_data.body.template)
     api.execute_created_content_actions(content)
     return api.get_content_in_context(content)
コード例 #20
0
ファイル: workspace_controller.py プロジェクト: tracim/tracim
 def create_generic_empty_content(
         self,
         context,
         request: TracimRequest,
         hapic_data=None,
 ) -> ContentInContext:
     """
     Creates a generic empty content. The minimum viable content has a label and a content type.
     Creating a content generally starts with a request to this endpoint.
     For specific contents like files, it is recommended to use the dedicated endpoint.
     This feature is accessible to contributors and higher role only.
     """
     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=content_type_list.Any_SLUG)  # 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_slug=creation_data.content_type,
         workspace=request.current_workspace,
         parent=parent,
     )
     api.save(content, ActionDescription.CREATION)
     content = api.get_content_in_context(content)
     return content
コード例 #21
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_slug=content_type_list.Folder.slug,
            workspace=business_workspace,
            label='Tools',
            do_save=True,
            do_notify=False,
        )
        menu_workspace = content_api.create(
            content_type_slug=content_type_list.Folder.slug,
            workspace=business_workspace,
            label='Menus',
            do_save=True,
            do_notify=False,
        )

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

        # Pages, threads, ..
        tiramisu_page = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            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_slug=content_type_list.Thread.slug,
            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_slug=content_type_list.File.slug,
            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_slug=content_type_list.File.slug,
            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_slug=content_type_list.Folder.slug,
            workspace=recipe_workspace,
            label='Fruits Desserts',
            parent=dessert_folder,
            do_save=True,
        )

        menu_page = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            workspace=business_workspace,
            parent=menu_workspace,
            label='Current Menu',
            do_save=True,
        )

        new_fruit_salad = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            workspace=recipe_workspace,
            parent=fruits_desserts_folder,
            label='New Fruit Salad',
            do_save=True,
        )
        old_fruit_salad = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            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_slug=content_type_list.Page.slug,
            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_slug=content_type_list.Page.slug,
            workspace=other_workspace,
            label='New Fruit Salad',
            do_save=True,
        )
        old_fruit_salad = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            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_slug=content_type_list.Page.slug,
            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()
コード例 #22
0
ファイル: content.py プロジェクト: tracim/tracim
    def insert(self):
        admin = self._session.query(User) \
            .filter(User.email == '*****@*****.**') \
            .one()
        bob = self._session.query(User) \
            .filter(User.email == '*****@*****.**') \
            .one()
        john_the_reader = self._session.query(User) \
            .filter(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_slug=content_type_list.Folder.slug,
            workspace=business_workspace,
            label='Tools',
            do_save=True,
            do_notify=False,
        )
        menu_workspace = content_api.create(
            content_type_slug=content_type_list.Folder.slug,
            workspace=business_workspace,
            label='Menus',
            do_save=True,
            do_notify=False,
        )

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

        # Pages, threads, ..
        tiramisu_page = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            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_slug=content_type_list.Thread.slug,
            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_slug=content_type_list.File.slug,
            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_slug=content_type_list.File.slug,
            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_slug=content_type_list.Folder.slug,
            workspace=recipe_workspace,
            label='Fruits Desserts',
            parent=dessert_folder,
            do_save=True,
        )

        menu_page = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            workspace=business_workspace,
            parent=menu_workspace,
            label='Current Menu',
            do_save=True,
        )

        new_fruit_salad = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            workspace=recipe_workspace,
            parent=fruits_desserts_folder,
            label='New Fruit Salad',
            do_save=True,
        )
        old_fruit_salad = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            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_slug=content_type_list.Page.slug,
            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_slug=content_type_list.Page.slug,
            workspace=other_workspace,
            label='New Fruit Salad',
            do_save=True,
        )
        old_fruit_salad = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            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_slug=content_type_list.Page.slug,
            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()
コード例 #23
0
ファイル: resources.py プロジェクト: inkhey/tracim
class ContentOnlyContainer(WebdavContainer):
    """
    Container that can get children content
    """
    def __init__(
        self,
        path: str,
        environ: dict,
        label: str,
        content: Content,
        provider: "TracimDavProvider",
        workspace: Workspace,
        tracim_context: "WebdavTracimContext",
    ) -> None:
        """
        Some rules:
        - if content given is None, return workspace root contents
        - if the given content is correct, return the subcontent of this content
         and user-known workspaces without any user-known parent to the list.
         - in case of content collision, only the first named content (sorted by content_id
         from lower to higher) will be returned.
        """
        self.path = path
        self.environ = environ
        self.workspace = workspace
        self.content = content
        self.tracim_context = tracim_context
        self.user = tracim_context.current_user
        self.session = tracim_context.dbsession
        self.label = label
        self.provider = provider
        self.content_api = ContentApi(
            current_user=self.user,
            session=tracim_context.dbsession,
            config=tracim_context.app_config,
            show_temporary=True,
            namespaces_filter=[ContentNamespaces.CONTENT],
        )

    # Internal methods
    def _get_members(
        self,
        already_existing_names: typing.Optional[typing.List[str]] = None
    ) -> typing.List[Content]:
        members_names = []
        members = []
        if self.content:
            parent_id = self.content.content_id
            children = self.content_api.get_all(
                content_type=content_type_list.Any_SLUG,
                workspace=self.workspace,
                parent_ids=[parent_id],
                order_by_properties=["content_id"],
            )
        else:
            children = self.content_api.get_all(
                content_type=content_type_list.Any_SLUG,
                workspace=self.workspace,
                parent_ids=[0],
                order_by_properties=["content_id"],
            )
        for child in children:
            if child.file_name in members_names:
                continue
            else:
                members_names.append(child.file_name)
                members.append(child)
        return members

    def _generate_child_content_resource(
            self, parent_path: str, child_content: Content) -> _DAVResource:
        content_path = "%s/%s" % (
            self.path,
            webdav_convert_file_name_to_display(child_content.file_name),
        )
        return get_content_resource(
            path=content_path,
            environ=self.environ,
            workspace=self.workspace,
            content=child_content,
            tracim_context=self.tracim_context,
        )

    # Container methods
    def createEmptyResource(self, file_name: str):
        """
        Create a new file on the current workspace/folder.
        """
        content = None
        fixed_file_name = webdav_convert_file_name_to_display(file_name)
        path = os.path.join(self.path, file_name)
        resource = self.provider.getResourceInst(path, self.environ)
        if resource:
            content = resource.content
        try:
            self.content_api.check_upload_size(
                int(self.environ["CONTENT_LENGTH"]), self.workspace)
        except (
                FileSizeOverMaxLimitation,
                FileSizeOverWorkspaceEmptySpace,
                FileSizeOverOwnerEmptySpace,
        ) as exc:
            raise DAVError(HTTP_REQUEST_ENTITY_TOO_LARGE, contextinfo=str(exc))
        # return item
        return FakeFileStream(
            session=self.session,
            file_name=fixed_file_name,
            content_api=self.content_api,
            workspace=self.workspace,
            content=content,
            parent=self.content,
            path=self.path + "/" + fixed_file_name,
        )

    def createCollection(self, label: str) -> "FolderResource":
        """
        Create a new folder for the current workspace/folder. 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.
        """
        folder_label = webdav_convert_file_name_to_bdd(label)
        try:
            folder = self.content_api.create(
                content_type_slug=content_type_list.Folder.slug,
                workspace=self.workspace,
                label=folder_label,
                parent=self.content,
            )
            self.content_api.execute_created_content_actions(folder)
        except TracimException as exc:
            raise DAVError(HTTP_FORBIDDEN, contextinfo=str(exc)) from exc

        self.content_api.save(folder)

        transaction.commit()
        # fixed_path
        folder_path = "%s/%s" % (self.path,
                                 webdav_convert_file_name_to_display(label))
        # return item
        return FolderResource(
            folder_path,
            self.environ,
            content=folder,
            tracim_context=self.tracim_context,
            workspace=self.workspace,
        )

    def getMemberNames(self) -> [str]:
        """
        Access to the list of content names for current workspace/folder
        """
        # INFO - G.M - 2020-14-10 - Unclear if this method is really used by wsgidav
        retlist = []
        for content in self._get_members():
            retlist.append(
                webdav_convert_file_name_to_display(content.file_name))
        return retlist

    def getMember(self, label: str) -> _DAVResource:
        """
        Access to a specific members
        """
        return self.provider.getResourceInst(
            "%s/%s" % (self.path, webdav_convert_file_name_to_display(label)),
            self.environ)

    def getMemberList(self) -> [_DAVResource]:
        """
        Access to the list of content of current workspace/folder
        """
        members = []
        for content in self._get_members():
            members.append(
                self._generate_child_content_resource(parent_path=self.path,
                                                      child_content=content))
        return members
コード例 #24
0
class TestFolderMove(FunctionalTest):
    def setUp(self):
        super().setUp()
        self.testapp.authorization = ("Basic", ("*****@*****.**",
                                                "*****@*****.**"))
        self.dbsession = get_tm_session(self.session_factory,
                                        transaction.manager)
        self.admin = self.dbsession.query(User).filter(
            User.email == "*****@*****.**").one()
        self.workspace_api = WorkspaceApi(current_user=self.admin,
                                          session=self.dbsession,
                                          config=self.app_config)
        self.content_api = ContentApi(current_user=self.admin,
                                      session=self.dbsession,
                                      config=self.app_config)
        self.workspace = self.workspace_api.create_workspace(label="test",
                                                             save_now=True)
        transaction.commit()

    def test_api__move_folder_into_itself__err_400(self) -> None:
        moved_folder = self.content_api.create(
            label="test_folder",
            content_type_slug=content_type_list.Folder.slug,
            workspace=self.workspace,
            do_save=True,
            do_notify=False,
        )
        transaction.commit()
        move_url = "/api/v2/workspaces/{}/contents/{}/move".format(
            self.workspace.workspace_id, moved_folder.content_id)
        body = {
            "new_parent_id": moved_folder.content_id,
            "new_workspace_id": self.workspace.workspace_id,
        }
        response = self.testapp.put_json(move_url, params=body, status=400)
        assert response.json_body[
            "code"] == ErrorCode.CONFLICTING_MOVE_IN_ITSELF

    def test_api__move_folder_in_a_direct_child__err_400(self) -> None:
        moved_folder = self.content_api.create(
            label="test_folder",
            content_type_slug=content_type_list.Folder.slug,
            workspace=self.workspace,
            do_save=True,
            do_notify=False,
        )
        child_folder = self.content_api.create(
            label="test_folder",
            content_type_slug=content_type_list.Folder.slug,
            workspace=self.workspace,
            parent=moved_folder,
            do_save=True,
            do_notify=False,
        )
        transaction.commit()
        move_url = "/api/v2/workspaces/{}/contents/{}/move".format(
            self.workspace.workspace_id, moved_folder.content_id)
        body = {
            "new_parent_id": child_folder.content_id,
            "new_workspace_id": self.workspace.workspace_id,
        }
        response = self.testapp.put_json(move_url, params=body, status=400)
        assert response.json_body[
            "code"] == ErrorCode.CONFLICTING_MOVE_IN_CHILD

    def test_api__move_folder_in_a_sub_child__err_400(self) -> None:
        moved_folder = self.content_api.create(
            label="test_folder",
            content_type_slug=content_type_list.Folder.slug,
            workspace=self.workspace,
            do_save=True,
            do_notify=False,
        )
        child_folder = self.content_api.create(
            label="child_test_folder",
            content_type_slug=content_type_list.Folder.slug,
            workspace=self.workspace,
            parent=moved_folder,
            do_save=True,
            do_notify=False,
        )
        sub_child_folder = self.content_api.create(
            label="sub_child_test_folder",
            content_type_slug=content_type_list.Folder.slug,
            workspace=self.workspace,
            parent=child_folder,
            do_save=True,
            do_notify=False,
        )
        transaction.commit()
        move_url = "/api/v2/workspaces/{}/contents/{}/move".format(
            self.workspace.workspace_id, moved_folder.content_id)
        body = {
            "new_parent_id": sub_child_folder.content_id,
            "new_workspace_id": self.workspace.workspace_id,
        }
        response = self.testapp.put_json(move_url, params=body, status=400)
        assert response.json_body[
            "code"] == ErrorCode.CONFLICTING_MOVE_IN_CHILD
コード例 #25
0
    def test_api___elasticsearch_search_ok__filter_by_content_type(
            self) -> None:
        dbsession = get_tm_session(self.session_factory, transaction.manager)
        admin = dbsession.query(User).filter(
            User.email == "*****@*****.**").one()
        uapi = UserApi(current_user=admin,
                       session=dbsession,
                       config=self.app_config)
        gapi = GroupApi(current_user=admin,
                        session=dbsession,
                        config=self.app_config)
        groups = [gapi.get_one_with_name("trusted-users")]
        user = uapi.create_user(
            "*****@*****.**",
            password="******",
            do_save=True,
            do_notify=False,
            groups=groups,
        )
        workspace_api = WorkspaceApi(current_user=admin,
                                     session=dbsession,
                                     config=self.app_config,
                                     show_deleted=True)
        workspace = workspace_api.create_workspace("test", save_now=True)
        rapi = RoleApi(current_user=admin,
                       session=dbsession,
                       config=self.app_config)
        rapi.create_one(user, workspace, UserRoleInWorkspace.WORKSPACE_MANAGER,
                        False)
        api = ContentApi(session=dbsession,
                         current_user=user,
                         config=self.app_config)
        doc = api.create(
            content_type_slug="html-document",
            workspace=workspace,
            label="stringtosearch doc",
            do_save=True,
        )
        api.execute_created_content_actions(doc)
        doc2 = api.create(
            content_type_slug="html-document",
            workspace=workspace,
            label="stringtosearch doc 2",
            do_save=True,
        )
        api.execute_created_content_actions(doc2)
        thread = api.create(
            content_type_slug="thread",
            workspace=workspace,
            label="stringtosearch thread",
            do_save=True,
        )
        api.execute_created_content_actions(thread)
        folder = api.create(
            content_type_slug="folder",
            workspace=workspace,
            label="stringtosearch folder",
            do_save=True,
        )
        api.execute_created_content_actions(folder)
        transaction.commit()
        self.refresh_elasticsearch()
        # get all
        params = {"search_string": "stringtosearch"}
        self.testapp.authorization = ("Basic", ("*****@*****.**",
                                                "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(),
                               status=200,
                               params=params)
        search_result = res.json_body
        assert search_result
        assert search_result["total_hits"] == 4
        assert search_result["is_total_hits_accurate"] is True
        assert len(search_result["contents"]) == 4

        params = {
            "search_string": "stringtosearch",
            "content_types": "html-document"
        }
        self.testapp.authorization = ("Basic", ("*****@*****.**",
                                                "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(),
                               status=200,
                               params=params)
        search_result = res.json_body
        assert search_result
        assert search_result["total_hits"] == 2
        assert search_result["is_total_hits_accurate"] is True
        assert len(search_result["contents"]) == 2
        labels = [content["label"] for content in search_result["contents"]]
        assert "stringtosearch doc 2" in labels
        assert "stringtosearch doc" in labels

        params = {
            "search_string": "stringtosearch",
            "content_types": "html-document,thread"
        }
        self.testapp.authorization = ("Basic", ("*****@*****.**",
                                                "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(),
                               status=200,
                               params=params)
        search_result = res.json_body
        assert search_result
        assert search_result["total_hits"] == 3
        assert search_result["is_total_hits_accurate"] is True
        assert len(search_result["contents"]) == 3
        labels = [content["label"] for content in search_result["contents"]]
        assert "stringtosearch doc 2" in labels
        assert "stringtosearch doc" in labels
        assert "stringtosearch doc 2" in labels
        assert "stringtosearch thread" in labels

        params = {"search_string": "stringtosearch", "content_types": "folder"}
        self.testapp.authorization = ("Basic", ("*****@*****.**",
                                                "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(),
                               status=200,
                               params=params)
        search_result = res.json_body
        assert search_result
        assert search_result["total_hits"] == 1
        assert search_result["is_total_hits_accurate"] is True
        assert len(search_result["contents"]) == 1
        assert search_result["contents"][0]["label"] == "stringtosearch folder"
コード例 #26
0
    def insert(self):
        admin = self._session.query(User).filter(User.email == "*****@*****.**").one()
        bob = self._session.query(User).filter(User.email == "*****@*****.**").one()
        john_the_reader = (
            self._session.query(User).filter(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

        content_api.create(
            content_type_slug=content_type_list.Folder.slug,
            workspace=business_workspace,
            label="Tools",
            do_save=True,
            do_notify=False,
        )
        menu_workspace = content_api.create(
            content_type_slug=content_type_list.Folder.slug,
            workspace=business_workspace,
            label="Menus",
            do_save=True,
            do_notify=False,
        )

        dessert_folder = content_api.create(
            content_type_slug=content_type_list.Folder.slug,
            workspace=recipe_workspace,
            label="Desserts",
            do_save=True,
            do_notify=False,
        )
        content_api.create(
            content_type_slug=content_type_list.Folder.slug,
            workspace=recipe_workspace,
            label="Salads",
            do_save=True,
            do_notify=False,
        )
        content_api.create(
            content_type_slug=content_type_list.Folder.slug,
            workspace=other_workspace,
            label="Infos",
            do_save=True,
            do_notify=False,
        )

        # Pages, threads, ..
        tiramisu_page = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            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>",
                new_label="Tiramisu Recipes!!!",
            )
            content_api.save(tiramisu_page, do_notify=False)

        best_cake_thread = content_api.create(
            content_type_slug=content_type_list.Thread.slug,
            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_slug=content_type_list.File.slug,
            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_slug=content_type_list.File.slug,
            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_slug=content_type_list.Folder.slug,
            workspace=recipe_workspace,
            label="Fruits Desserts",
            parent=dessert_folder,
            do_save=True,
            do_notify=False,
        )

        content_api.create(
            content_type_slug=content_type_list.Page.slug,
            workspace=business_workspace,
            parent=menu_workspace,
            label="Current Menu",
            do_save=True,
            do_notify=False,
        )

        content_api.create(
            content_type_slug=content_type_list.Page.slug,
            workspace=recipe_workspace,
            parent=fruits_desserts_folder,
            label="New Fruit Salad",
            do_save=True,
            do_notify=False,
        )
        old_fruit_salad = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            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, do_notify=False)

        bad_fruit_salad = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            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, do_notify=False)

        # File at the root for test
        content_api.create(
            content_type_slug=content_type_list.Page.slug,
            workspace=other_workspace,
            label="New Fruit Salad",
            do_save=True,
            do_notify=False,
        )
        old_fruit_salad = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            workspace=other_workspace,
            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, do_notify=False)

        bad_fruit_salad = content_api.create(
            content_type_slug=content_type_list.Page.slug,
            workspace=other_workspace,
            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, do_notify=False)

        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>",
            do_save=True,
            do_notify=False,
        )
        bob_content_api.create_comment(
            parent=best_cake_thread,
            content="<p>What about Apple Pie? There are Awesome!</p>",
            do_save=True,
            do_notify=False,
        )
        reader_content_api.create_comment(
            parent=best_cake_thread,
            content="<p>You are right, but Kouign-amann are clearly better.</p>",
            do_save=True,
            do_notify=False,
        )
        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, do_notify=False)

        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>",
                new_label="Tiramisu Recipe",
            )
            bob_content_api.save(tiramisu_page, do_notify=False)
        self._session.flush()
コード例 #27
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,
        label: str,
        path: str,
        environ: dict,
        workspace: Workspace,
        tracim_context: "WebdavTracimContext",
    ) -> None:
        super(WorkspaceResource, self).__init__(path, environ)

        self.workspace = workspace
        self.content = None
        self.tracim_context = tracim_context
        self.user = tracim_context.current_user
        self.session = tracim_context.dbsession
        self.label = label
        self.content_api = ContentApi(
            current_user=self.user,
            session=tracim_context.dbsession,
            config=tracim_context.app_config,
            show_temporary=True,
            namespaces_filter=[ContentNamespaces.CONTENT],
        )

        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 webdav_convert_file_name_to_display(self.label)

    def getDisplayInfo(self):
        return {"type": "workspace".capitalize()}

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

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

        children = self.content_api.get_all(
            parent_ids=[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 != content_type_list.Folder.slug:
                self._file_count += 1
            retlist.append(
                webdav_convert_file_name_to_display(content.file_name))

        return retlist

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

        return self.provider.getResourceInst(
            "%s/%s" %
            (self.path, webdav_convert_file_name_to_display(content_label)),
            self.environ)

    @webdav_check_right(is_contributor)
    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
        # fixed path
        fixed_file_name = webdav_convert_file_name_to_display(file_name)
        path = os.path.join(self.path, file_name)
        resource = self.provider.getResourceInst(path, self.environ)
        if resource:
            content = resource.content
        try:
            self.content_api.check_upload_size(
                int(self.environ["CONTENT_LENGTH"]), self.workspace)
        except (
                FileSizeOverMaxLimitation,
                FileSizeOverWorkspaceEmptySpace,
                FileSizeOverOwnerEmptySpace,
        ) as exc:
            raise DAVError(HTTP_REQUEST_ENTITY_TOO_LARGE, contextinfo=str(exc))
        # return item
        return FakeFileStream(
            session=self.session,
            file_name=fixed_file_name,
            content_api=self.content_api,
            workspace=self.workspace,
            content=content,
            parent=self.content,
            path=self.path + "/" + fixed_file_name,
        )

    @webdav_check_right(is_content_manager)
    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_label = webdav_convert_file_name_to_bdd(label)
        try:
            folder = self.content_api.create(
                content_type_slug=content_type_list.Folder.slug,
                workspace=self.workspace,
                label=folder_label,
                parent=self.content,
            )
            self.content_api.execute_created_content_actions(folder)
        except TracimException as exc:
            raise DAVError(HTTP_FORBIDDEN, contextinfo=str(exc)) from exc

        self.content_api.save(folder)

        transaction.commit()
        # fixed_path
        folder_path = "%s/%s" % (self.path,
                                 webdav_convert_file_name_to_display(label))
        # return item
        return FolderResource(
            folder_path,
            self.environ,
            content=folder,
            tracim_context=self.tracim_context,
            workspace=self.workspace,
        )

    def delete(self):
        """For now, it is not possible to delete a workspace through the webdav client."""
        # FIXME - G.M - 2018-12-11 - For an unknown reason current_workspace
        # of tracim_context is here invalid.
        self.tracim_context._current_workspace = self.workspace
        try:
            can_delete_workspace.check(self.tracim_context)
        except TracimException as exc:
            raise DAVError(HTTP_FORBIDDEN, contextinfo=str(exc))
        raise DAVError(HTTP_FORBIDDEN,
                       "Workspace deletion is not allowed through webdav")

    def supportRecursiveMove(self, destpath):
        return True

    def moveRecursive(self, destpath):
        # INFO - G.M - 2018-12-11 - We only allow renaming
        if dirname(normpath(
                destpath)) == self.environ["http_authenticator.realm"]:
            # FIXME - G.M - 2018-12-11 - For an unknown reason current_workspace
            # of tracim_context is here invalid.
            self.tracim_context._current_workspace = self.workspace
            try:
                can_modify_workspace.check(self.tracim_context)
            except TracimException as exc:
                raise DAVError(HTTP_FORBIDDEN, contextinfo=str(exc))

            try:
                workspace_api = WorkspaceApi(current_user=self.user,
                                             session=self.session,
                                             config=self.provider.app_config)
                workspace_api.update_workspace(
                    workspace=self.workspace,
                    label=webdav_convert_file_name_to_bdd(
                        basename(normpath(destpath))),
                    description=self.workspace.description,
                )
                self.session.add(self.workspace)
                self.session.flush()
                workspace_api.execute_update_workspace_actions(self.workspace)
                transaction.commit()
            except TracimException as exc:
                raise DAVError(HTTP_FORBIDDEN, contextinfo=str(exc))

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

        children = self.content_api.get_all(False, content_type_list.Any_SLUG,
                                            self.workspace)

        for content in children:
            content_path = "%s/%s" % (
                self.path,
                webdav_convert_file_name_to_display(content.file_name),
            )

            if content.type == content_type_list.Folder.slug:
                members.append(
                    FolderResource(
                        path=content_path,
                        environ=self.environ,
                        workspace=self.workspace,
                        content=content,
                        tracim_context=self.tracim_context,
                    ))
            elif content.type == content_type_list.File.slug:
                self._file_count += 1
                members.append(
                    FileResource(
                        path=content_path,
                        environ=self.environ,
                        content=content,
                        tracim_context=self.tracim_context,
                    ))
            else:
                self._file_count += 1
                members.append(
                    OtherFileResource(content_path,
                                      self.environ,
                                      content,
                                      tracim_context=self.tracim_context))
        return members
コード例 #28
0
ファイル: test_comments.py プロジェクト: tracim/tracim
 def test_api__post_content_comment__err_400__content_not_editable(self) -> None:
     """
     Get alls comments of a content
     """
     dbsession = get_tm_session(self.session_factory, transaction.manager)
     admin = dbsession.query(User) \
         .filter(User.email == '*****@*****.**') \
         .one() # type: User
     workspace_api = WorkspaceApi(
         current_user=admin,
         session=dbsession,
         config=self.app_config
     )
     business_workspace = workspace_api.get_one(1)
     content_api = ContentApi(
         current_user=admin,
         session=dbsession,
         config=self.app_config
     )
     tool_folder = content_api.get_one(1, content_type=content_type_list.Any_SLUG)
     test_thread = content_api.create(
         content_type_slug=content_type_list.Thread.slug,
         workspace=business_workspace,
         parent=tool_folder,
         label='Test Thread',
         do_save=True,
         do_notify=False,
     )
     with new_revision(
        session=dbsession,
        tm=transaction.manager,
        content=test_thread,
     ):
         content_api.update_content(
             test_thread,
             new_label='test_thread_updated',
             new_content='Just a test'
         )
     content_api.set_status(test_thread, 'closed-deprecated')
     transaction.commit()
     self.testapp.authorization = (
         'Basic',
         (
             '*****@*****.**',
             '*****@*****.**'
         )
     )
     params = {
         'raw_content': 'I strongly disagree, Tiramisu win!'
     }
     res = self.testapp.post_json(
         '/api/v2/workspaces/{}/contents/{}/comments'.format(
             business_workspace.workspace_id,
             test_thread.content_id
         ),
         params=params,
         status=400
     )
     assert res.json_body
     assert 'code' in res.json_body
     assert res.json_body['code'] == error.CONTENT_IN_NOT_EDITABLE_STATE
コード例 #29
0
ファイル: test_comments.py プロジェクト: tracim/tracim
 def test_api__post_content_comment__ok_200__nominal_case(self) -> None:
     """
     Get alls comments of a content
     """
     dbsession = get_tm_session(self.session_factory, transaction.manager)
     admin = dbsession.query(User) \
         .filter(User.email == '*****@*****.**') \
         .one() # type: User
     workspace_api = WorkspaceApi(
         current_user=admin,
         session=dbsession,
         config=self.app_config
     )
     business_workspace = workspace_api.get_one(1)
     content_api = ContentApi(
         current_user=admin,
         session=dbsession,
         config=self.app_config
     )
     tool_folder = content_api.get_one(1, content_type=content_type_list.Any_SLUG)
     test_thread = content_api.create(
         content_type_slug=content_type_list.Thread.slug,
         workspace=business_workspace,
         parent=tool_folder,
         label='Test Thread',
         do_save=True,
         do_notify=False,
     )
     with new_revision(
        session=dbsession,
        tm=transaction.manager,
        content=test_thread,
     ):
         content_api.update_content(
             test_thread,
             new_label='test_thread_updated',
             new_content='Just a test'
         )
     transaction.commit()
     self.testapp.authorization = (
         'Basic',
         (
             '*****@*****.**',
             '*****@*****.**'
         )
     )
     params = {
         'raw_content': 'I strongly disagree, Tiramisu win!'
     }
     res = self.testapp.post_json(
         '/api/v2/workspaces/{}/contents/{}/comments'.format(
             business_workspace.workspace_id,
             test_thread.content_id
         ),
         params=params,
         status=200
     )
     comment = res.json_body
     assert comment['content_id']
     assert comment['parent_id'] == test_thread.content_id
     assert comment['raw_content'] == 'I strongly disagree, Tiramisu win!'
     assert comment['author']
     assert comment['author']['user_id'] == admin.user_id
     # TODO - G.M - 2018-06-172 - [avatar] setup avatar url
     assert comment['author']['avatar_url'] is None
     assert comment['author']['public_name'] == admin.display_name
     # TODO - G.M - 2018-06-179 - better check for datetime
     assert comment['created']
コード例 #30
0
    def test_api___simple_search_ok__filter_by_deleted_archived_active(self) -> None:
        dbsession = get_tm_session(self.session_factory, transaction.manager)
        admin = dbsession.query(User).filter(User.email == "*****@*****.**").one()
        uapi = UserApi(current_user=admin, session=dbsession, config=self.app_config)
        gapi = GroupApi(current_user=admin, session=dbsession, config=self.app_config)
        groups = [gapi.get_one_with_name("trusted-users")]
        user = uapi.create_user(
            "*****@*****.**",
            password="******",
            do_save=True,
            do_notify=False,
            groups=groups,
        )
        workspace_api = WorkspaceApi(
            current_user=admin, session=dbsession, config=self.app_config, show_deleted=True
        )
        workspace = workspace_api.create_workspace("test", save_now=True)
        rapi = RoleApi(current_user=admin, session=dbsession, config=self.app_config)
        rapi.create_one(user, workspace, UserRoleInWorkspace.WORKSPACE_MANAGER, False)
        api = ContentApi(session=dbsession, current_user=user, config=self.app_config)
        api.create(
            content_type_slug="html-document",
            workspace=workspace,
            label="stringtosearch active",
            do_save=True,
        )
        api.create(
            content_type_slug="html-document",
            workspace=workspace,
            label="stringtosearch active 2",
            do_save=True,
        )
        deleted_content = api.create(
            content_type_slug="html-document",
            workspace=workspace,
            label="stringtosearch deleted",
            do_save=True,
        )
        with new_revision(session=dbsession, tm=transaction.manager, content=deleted_content):
            api.delete(deleted_content)
        api.save(deleted_content)
        archived_content = api.create(
            content_type_slug="html-document",
            workspace=workspace,
            label="stringtosearch archived",
            do_save=True,
        )
        with new_revision(session=dbsession, tm=transaction.manager, content=archived_content):
            api.archive(archived_content)
        api.save(archived_content)
        transaction.commit()

        # get all
        params = {
            "search_string": "stringtosearch",
            "show_deleted": 1,
            "show_archived": 1,
            "show_active": 1,
        }
        self.testapp.authorization = ("Basic", ("*****@*****.**", "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(), status=200, params=params)
        search_result = res.json_body
        assert search_result
        assert search_result["total_hits"] == 4
        assert search_result["is_total_hits_accurate"] is False
        assert len(search_result["contents"]) == 4

        # get only active
        params = {"search_string": "stringtosearch"}
        self.testapp.authorization = ("Basic", ("*****@*****.**", "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(), status=200, params=params)
        default_search_result = res.json_body
        assert default_search_result
        assert default_search_result["total_hits"] == 2
        assert default_search_result["is_total_hits_accurate"] is False
        assert len(default_search_result["contents"]) == 2
        assert default_search_result["contents"][0]["label"] == "stringtosearch active 2"
        assert default_search_result["contents"][1]["label"] == "stringtosearch active"

        params = {
            "search_string": "stringtosearch",
            "show_active": 1,
            "show_deleted": 0,
            "show_archived": 0,
        }
        self.testapp.authorization = ("Basic", ("*****@*****.**", "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(), status=200, params=params)
        only_active_search_result = res.json_body
        assert only_active_search_result == default_search_result

        params = {
            "search_string": "stringtosearch",
            "show_active": 1,
            "show_deleted": 1,
            "show_archived": 0,
        }
        self.testapp.authorization = ("Basic", ("*****@*****.**", "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(), status=200, params=params)
        search_result = res.json_body
        assert search_result
        assert search_result["total_hits"] == 3
        assert search_result["is_total_hits_accurate"] is False
        assert len(search_result["contents"]) == 3
        assert search_result["contents"][0]["label"].startswith("stringtosearch deleted")
        assert search_result["contents"][1]["label"] == "stringtosearch active 2"
        assert search_result["contents"][2]["label"] == "stringtosearch active"

        params = {
            "search_string": "stringtosearch",
            "show_active": 0,
            "show_deleted": 0,
            "show_archived": 1,
        }
        self.testapp.authorization = ("Basic", ("*****@*****.**", "*****@*****.**"))
        res = self.testapp.get("/api/v2/search/content".format(), status=200, params=params)
        search_result = res.json_body
        assert search_result
        assert search_result["total_hits"] == 1
        assert search_result["is_total_hits_accurate"] is False
        assert len(search_result["contents"]) == 1
        assert search_result["contents"][0]["label"].startswith("stringtosearch archived")
コード例 #31
0
    def upload_files(
        self,
        upload_permission: UploadPermission,
        uploader_username: str,
        message: typing.Optional[str],
        files: typing.List[cgi.FieldStorage],
        do_notify: bool = False,
    ) -> typing.List[ContentInContext]:
        content_api = ContentApi(
            config=self._config,
            current_user=upload_permission.author,
            session=self._session,
            namespaces_filter=[ContentNamespaces.UPLOAD],
        )
        translator = Translator(app_config=self._config)
        _ = translator.get_translation
        current_datetime = datetime.utcnow()
        folder_label = _(
            "Files uploaded by {username} on {date} at {time}").format(
                username=uploader_username,
                date=format_date(current_datetime,
                                 locale=translator.default_lang),
                time=format_time(current_datetime,
                                 locale=translator.default_lang),
            )

        try:
            upload_folder = content_api.create(
                content_type_slug=content_type_list.Folder.slug,
                workspace=upload_permission.workspace,
                label=folder_label,
                do_notify=False,
                do_save=True,
                content_namespace=ContentNamespaces.UPLOAD,
            )
        except ContentFilenameAlreadyUsedInFolder:
            upload_folder = content_api.get_one_by_filename(
                filename=folder_label, workspace=upload_permission.workspace)

        created_contents = []
        if message:
            comment_message = _("Message from {username}: {message}").format(
                username=uploader_username, message=message)
        else:
            comment_message = _("Uploaded by {username}.").format(
                username=uploader_username)

        for _file in files:
            content = content_api.create(
                filename=_file.filename,
                content_type_slug=content_type_list.File.slug,
                workspace=upload_permission.workspace,
                parent=upload_folder,
                do_notify=False,
                content_namespace=ContentNamespaces.UPLOAD,
            )
            content_api.save(content, ActionDescription.CREATION)
            with new_revision(session=self._session,
                              tm=transaction.manager,
                              content=content):
                content_api.update_file_data(
                    content,
                    new_filename=_file.filename,
                    new_mimetype=_file.type,
                    new_content=_file.file,
                )
            content_api.create_comment(parent=content,
                                       content=comment_message,
                                       do_save=True,
                                       do_notify=False)
            created_contents.append(
                content_api.get_content_in_context(content))
            content_api.execute_created_content_actions(content)

        if do_notify:
            workspace_lib = WorkspaceApi(config=self._config,
                                         current_user=upload_permission.author,
                                         session=self._session)
            self._notify_uploaded_contents(
                uploader_username=uploader_username,
                workspace_in_context=workspace_lib.get_workspace_with_context(
                    upload_permission.workspace),
                uploader_message=message,
                uploaded_contents=created_contents,
                uploader_email=upload_permission.email,
            )

        return created_contents
コード例 #32
0
ファイル: resources.py プロジェクト: tracim/tracim
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,
                 tracim_context: 'WebdavTracimContext'
    ) -> None:
        super(WorkspaceResource, self).__init__(path, environ)

        self.workspace = workspace
        self.content = None
        self.tracim_context = tracim_context
        self.user = tracim_context.current_user
        self.session = tracim_context.dbsession
        self.content_api = ContentApi(
            current_user=self.user,
            session=tracim_context.dbsession,
            config=tracim_context.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 webdav_convert_file_name_to_display(self.workspace.label)

    def getDisplayInfo(self):
        return {
            'type': "workspace".capitalize(),
        }

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

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

        children = self.content_api.get_all(
            parent_ids=[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 != content_type_list.Folder.slug:
                self._file_count += 1
            retlist.append(webdav_convert_file_name_to_display(content.file_name))

        return retlist

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

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

    @webdav_check_right(is_contributor)
    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
        # fixed path
        fixed_file_name = webdav_convert_file_name_to_display(file_name)
        path = os.path.join(self.path, file_name)
        resource = self.provider.getResourceInst(path, self.environ)
        if resource:
            content = resource.content

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

    @webdav_check_right(is_content_manager)
    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_label = webdav_convert_file_name_to_bdd(label)
        try:
            folder = self.content_api.create(
                content_type_slug=content_type_list.Folder.slug,
                workspace=self.workspace,
                label=folder_label,
                parent=self.content
            )
        except TracimException as exc:
            raise DAVError(HTTP_FORBIDDEN) from exc

        self.content_api.save(folder)

        transaction.commit()
        # fixed_path
        folder_path = '%s/%s' % (self.path, webdav_convert_file_name_to_display(label))
        # return item
        return FolderResource(
            folder_path,
            self.environ,
            content=folder,
            tracim_context=self.tracim_context,
            workspace=self.workspace,
        )

    def delete(self):
        """For now, it is not possible to delete a workspace through the webdav client."""
        # FIXME - G.M - 2018-12-11 - For an unknown reason current_workspace
        # of tracim_context is here invalid.
        self.tracim_context._current_workspace = self.workspace
        try:
            can_delete_workspace.check(self.tracim_context)
        except TracimException as exc:
            raise DAVError(HTTP_FORBIDDEN)
        raise DAVError(HTTP_FORBIDDEN)

    def supportRecursiveMove(self, destpath):
        return True

    def moveRecursive(self, destpath):
        # INFO - G.M - 2018-12-11 - We only allow renaming
        if dirname(normpath(destpath)) == self.environ['http_authenticator.realm']:
            # FIXME - G.M - 2018-12-11 - For an unknown reason current_workspace
            # of tracim_context is here invalid.
            self.tracim_context._current_workspace = self.workspace
            try:
                can_modify_workspace.check(self.tracim_context)
            except TracimException as exc:
                raise DAVError(HTTP_FORBIDDEN)

            try:
                self.workspace.label = webdav_convert_file_name_to_bdd(basename(normpath(destpath)))
                self.session.add(self.workspace)
                self.session.flush()
                transaction.commit()
            except TracimException as exc:
                raise DAVError(HTTP_FORBIDDEN)

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

        children = self.content_api.get_all(False, content_type_list.Any_SLUG, self.workspace)

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

            if content.type == content_type_list.Folder.slug:
                members.append(
                    FolderResource(
                        path=content_path,
                        environ=self.environ,
                        workspace=self.workspace,
                        content=content,
                        tracim_context=self.tracim_context
                    )
                )
            elif content.type == content_type_list.File.slug:
                self._file_count += 1
                members.append(
                    FileResource(
                        path=content_path,
                        environ=self.environ,
                        content=content,
                        tracim_context=self.tracim_context
                    )
                )
            else:
                self._file_count += 1
                members.append(
                    OtherFileResource(
                        content_path,
                        self.environ,
                        content,
                        tracim_context=self.tracim_context
                    ))
        return members