Пример #1
0
 def contents_read_status(self,
                          context,
                          request: TracimRequest,
                          hapic_data=None):  # nopep8
     """
     get user_read status of contents
     """
     app_config = request.registry.settings['CFG']
     content_filter = hapic_data.query
     api = ContentApi(
         current_user=request.candidate_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     wapi = WorkspaceApi(
         current_user=request.candidate_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     workspace = None
     if hapic_data.path.workspace_id:
         workspace = wapi.get_one(hapic_data.path.workspace_id)
     last_actives = api.get_last_active(
         workspace=workspace,
         limit=None,
         before_content=None,
         content_ids=hapic_data.query.contents_ids or None)
     return [
         api.get_content_in_context(content) for content in last_actives
     ]
Пример #2
0
 def last_active_content(self,
                         context,
                         request: TracimRequest,
                         hapic_data=None):
     """
     Get last_active_content for user
     """
     app_config = request.registry.settings["CFG"]  # type: CFG
     content_filter = hapic_data.query
     api = ContentApi(
         current_user=request.candidate_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     wapi = WorkspaceApi(
         current_user=request.candidate_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     workspace = None
     if hapic_data.path.workspace_id:
         workspace = wapi.get_one(hapic_data.path.workspace_id)
     before_content = None
     if content_filter.before_content_id:
         before_content = api.get_one(
             content_id=content_filter.before_content_id,
             workspace=workspace,
             content_type=content_type_list.Any_SLUG,
         )
     last_actives = api.get_last_active(workspace=workspace,
                                        limit=content_filter.limit or None,
                                        before_content=before_content)
     return [
         api.get_content_in_context(content) for content in last_actives
     ]
Пример #3
0
    def _create_content_event(self, operation: OperationType, content: Content,
                              context: TracimContext) -> None:
        current_user = context.safe_current_user()
        content_api = ContentApi(context.dbsession, current_user, self._config)
        content_in_context = content_api.get_content_in_context(content)
        content_schema = EventApi.get_content_schema_for_type(content.type)
        content_dict = content_schema.dump(content_in_context).data

        workspace_api = WorkspaceApi(context.dbsession,
                                     current_user,
                                     self._config,
                                     show_deleted=True)
        workspace_in_context = workspace_api.get_workspace_with_context(
            workspace_api.get_one(content_in_context.workspace.workspace_id))
        fields = {
            Event.CONTENT_FIELD:
            content_dict,
            Event.WORKSPACE_FIELD:
            EventApi.workspace_schema.dump(workspace_in_context).data,
        }
        event_api = EventApi(current_user, context.dbsession, self._config)
        event_api.create_event(
            entity_type=EntityType.CONTENT,
            operation=operation,
            additional_fields=fields,
            entity_subtype=content.type,
            context=context,
        )
Пример #4
0
 def enable_workspace_notification(self,
                                   context,
                                   request: TracimRequest,
                                   hapic_data=None):  # nopep8
     """
     enable workspace notification
     """
     app_config = request.registry.settings['CFG']
     api = ContentApi(
         current_user=request.candidate_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     wapi = WorkspaceApi(
         current_user=request.candidate_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     workspace = wapi.get_one(hapic_data.path.workspace_id)
     wapi.enable_notifications(request.candidate_user, workspace)
     rapi = RoleApi(
         current_user=request.candidate_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     role = rapi.get_one(request.candidate_user.user_id,
                         workspace.workspace_id)
     wapi.save(workspace)
     return
Пример #5
0
 def delete_comment(self, context, request: TracimRequest, hapic_data=None):
     """
     Delete comment
     """
     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,
     )
     wapi = WorkspaceApi(
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config,
     )
     workspace = wapi.get_one(hapic_data.path.workspace_id)
     parent = api.get_one(hapic_data.path.content_id,
                          content_type=content_type_list.Any_SLUG,
                          workspace=workspace)
     comment = api.get_one(
         hapic_data.path.comment_id,
         content_type=content_type_list.Comment.slug,
         workspace=workspace,
         parent=parent,
     )
     with new_revision(session=request.dbsession,
                       tm=transaction.manager,
                       content=comment):
         api.delete(comment)
     return
Пример #6
0
 def _create_subscription_event(self, operation: OperationType,
                                subscription: WorkspaceSubscription,
                                context: TracimContext) -> None:
     current_user = context.safe_current_user()
     workspace_api = WorkspaceApi(
         session=context.dbsession,
         config=self._config,
         current_user=None,
     )
     workspace_in_context = workspace_api.get_workspace_with_context(
         workspace_api.get_one(subscription.workspace_id))
     user_api = UserApi(current_user,
                        context.dbsession,
                        self._config,
                        show_deleted=True)
     subscription_author_in_context = user_api.get_user_with_context(
         subscription.author)
     fields = {
         Event.WORKSPACE_FIELD:
         EventApi.workspace_schema.dump(workspace_in_context).data,
         Event.SUBSCRIPTION_FIELD:
         EventApi.workspace_subscription_schema.dump(subscription).data,
         Event.USER_FIELD:
         EventApi.user_schema.dump(subscription_author_in_context).data,
     }
     event_api = EventApi(current_user, context.dbsession, self._config)
     event_api.create_event(
         entity_type=EntityType.WORKSPACE_SUBSCRIPTION,
         operation=operation,
         additional_fields=fields,
         context=context,
     )
Пример #7
0
    def guest_download_check(self,
                             context,
                             request: TracimRequest,
                             hapic_data=None) -> None:
        """
        Check if share token is correct and password given valid
        """
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ShareLib(current_user=None,
                       session=request.dbsession,
                       config=app_config)
        content_share = api.get_content_share_by_token(
            share_token=hapic_data.path.share_token)  # type: ContentShare

        # TODO - G.M - 2019-08-01 - verify in access to content share can be granted
        # we should considered do these check at decorator level
        api.check_password(content_share, password=hapic_data.body.password)
        content = ContentApi(current_user=None,
                             session=request.dbsession,
                             config=app_config).get_one(
                                 content_share.content_id,
                                 content_type=content_type_list.Any_SLUG)
        workspace_api = WorkspaceApi(current_user=None,
                                     session=request.dbsession,
                                     config=app_config)
        workspace = workspace_api.get_one(content.workspace_id)
        workspace_api.check_public_download_enabled(workspace)
        if content.type not in shareables_content_type:
            raise ContentTypeNotAllowed()
Пример #8
0
 def account_contents_read_status(self,
                                  context,
                                  request: TracimRequest,
                                  hapic_data=None):
     """
     get user_read status of contents
     """
     app_config = request.registry.settings["CFG"]  # type: CFG
     api = ContentApi(
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config  # User
     )
     wapi = WorkspaceApi(
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config  # User
     )
     workspace = None
     if hapic_data.path.workspace_id:
         workspace = wapi.get_one(hapic_data.path.workspace_id)
     last_actives = api.get_last_active(
         workspace=workspace,
         limit=None,
         before_content=None,
         content_ids=hapic_data.query.content_ids or None,
     )
     return [
         api.get_content_in_context(content) for content in last_actives
     ]
Пример #9
0
 def account_last_active_content(self, context, request: TracimRequest, hapic_data=None):  # nopep8
     """
     Get last_active_content for user
     """
     app_config = request.registry.settings['CFG']
     content_filter = hapic_data.query
     api = ContentApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     wapi = WorkspaceApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     workspace = None
     if hapic_data.path.workspace_id:
         workspace = wapi.get_one(hapic_data.path.workspace_id)
     before_content = None
     if content_filter.before_content_id:
         before_content = api.get_one(
             content_id=content_filter.before_content_id,
             workspace=workspace,
             content_type=content_type_list.Any_SLUG
         )
     last_actives = api.get_last_active(
         workspace=workspace,
         limit=content_filter.limit or None,
         before_content=before_content,
     )
     return [
         api.get_content_in_context(content)
         for content in last_actives
     ]
Пример #10
0
 def account_contents_read_status(self, context, request: TracimRequest, hapic_data=None):  # nopep8
     """
     get user_read status of contents
     """
     app_config = request.registry.settings['CFG']
     content_filter = hapic_data.query
     api = ContentApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     wapi = WorkspaceApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     workspace = None
     if hapic_data.path.workspace_id:
         workspace = wapi.get_one(hapic_data.path.workspace_id)
     last_actives = api.get_last_active(
         workspace=workspace,
         limit=None,
         before_content=None,
         content_ids=hapic_data.query.content_ids or None
     )
     return [
         api.get_content_in_context(content)
         for content in last_actives
     ]
Пример #11
0
 def _get_current_workspace(
         self,
         user: User,
         request: 'TracimRequest'
 ) -> Workspace:
     """
     Get current workspace from request
     :param user: User who want to check the workspace
     :param request: pyramid request
     :return: current workspace
     """
     workspace_id = ''
     try:
         if 'workspace_id' in request.matchdict:
             workspace_id_str = request.matchdict['workspace_id']
             if not isinstance(workspace_id_str, str) or not workspace_id_str.isdecimal():  # nopep8
                 raise InvalidWorkspaceId('workspace_id is not a correct integer')  # nopep8
             workspace_id = int(request.matchdict['workspace_id'])
         if not workspace_id:
             raise WorkspaceNotFoundInTracimRequest('No workspace_id property found in request')  # nopep8
         wapi = WorkspaceApi(
             current_user=user,
             session=request.dbsession,
             config=request.registry.settings['CFG'],
             show_deleted=True,
         )
         workspace = wapi.get_one(workspace_id)
     except NoResultFound as exc:
         raise WorkspaceNotFound(
             'Workspace {} does not exist '
             'or is not visible for this user'.format(workspace_id)
         ) from exc
     return workspace
Пример #12
0
 def create_workspace(self,
                      context,
                      request: TracimRequest,
                      hapic_data=None):
     """
     Create a workspace. This route is for trusted users and administrators.
     """
     app_config = request.registry.settings["CFG"]  # type: CFG
     wapi = WorkspaceApi(
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config  # User
     )
     parent = None
     if hapic_data.body.parent_id:
         parent = wapi.get_one(workspace_id=hapic_data.body.parent_id)
     workspace = wapi.create_workspace(
         label=hapic_data.body.label,
         description=hapic_data.body.description,
         access_type=hapic_data.body.access_type,
         save_now=True,
         agenda_enabled=hapic_data.body.agenda_enabled,
         public_download_enabled=hapic_data.body.public_download_enabled,
         public_upload_enabled=hapic_data.body.public_upload_enabled,
         default_user_role=hapic_data.body.default_user_role,
         parent=parent,
     )
     wapi.execute_created_workspace_actions(workspace)
     return wapi.get_workspace_with_context(workspace)
Пример #13
0
 def enable_account_workspace_notification(self, context, request: TracimRequest, hapic_data=None):  # nopep8
     """
     enable workspace notification
     """
     app_config = request.registry.settings['CFG']
     api = ContentApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     wapi = WorkspaceApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     workspace = wapi.get_one(hapic_data.path.workspace_id)
     wapi.enable_notifications(request.current_user, workspace)
     rapi = RoleApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     role = rapi.get_one(request.current_user.user_id, workspace.workspace_id)  # nopep8
     wapi.save(workspace)
     return
Пример #14
0
 def _get_workspace(self, workspace_id_fetcher):
     workspace_id = workspace_id_fetcher()
     wapi = WorkspaceApi(
         current_user=self.current_user,
         session=self.dbsession,
         config=self.app_config,
         show_deleted=True,
     )
     return wapi.get_one(workspace_id)
Пример #15
0
 def _get_workspace(self, workspace_id_fetcher):
     workspace_id = workspace_id_fetcher()
     wapi = WorkspaceApi(
         current_user=self.current_user,
         session=self.dbsession,
         config=self.app_config,
         show_deleted=True,
     )
     return wapi.get_one(workspace_id)
Пример #16
0
    def guest_download_file(self,
                            context,
                            request: TracimRequest,
                            hapic_data=None) -> HapicFile:
        app_config = request.registry.settings["CFG"]  # type: CFG
        api = ShareLib(current_user=None,
                       session=request.dbsession,
                       config=app_config)
        content_share = api.get_content_share_by_token(
            share_token=hapic_data.path.share_token)  # type: ContentShare

        # TODO - G.M - 2019-08-01 - verify in access to content share can be granted
        # we should considered do these check at decorator level
        if hapic_data.forms:
            password = hapic_data.forms.password
        else:
            password = None
        api.check_password(content_share, password=password)
        content = ContentApi(current_user=None,
                             session=request.dbsession,
                             config=app_config).get_one(
                                 content_share.content_id,
                                 content_type=content_type_list.Any_SLUG)
        workspace_api = WorkspaceApi(current_user=None,
                                     session=request.dbsession,
                                     config=app_config)
        workspace = workspace_api.get_one(content.workspace_id)
        workspace_api.check_public_download_enabled(workspace)
        if content.type not in shareables_content_type:
            raise ContentTypeNotAllowed()

        try:
            file = DepotManager.get(
                app_config.UPLOADED_FILES__STORAGE__STORAGE_NAME).get(
                    content.depot_file)
        except IOError as exc:
            raise TracimFileNotFound(
                "file related to revision {} of content {} not found in depot."
                .format(content.cached_revision_id,
                        content.content_id)) from exc
        filename = hapic_data.path.filename

        # INFO - G.M - 2019-08-08 - use given filename in all case but none or
        # "raw", when filename returned will be original file one.
        if not filename or filename == "raw":
            filename = content.file_name
        return HapicFile(
            file_object=file,
            mimetype=file.content_type,
            filename=filename,
            as_attachment=True,
            content_length=file.content_length,
            last_modified=content.updated,
        )
Пример #17
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(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']
Пример #18
0
 def disable_workspace_notification(self, context, request: TracimRequest, hapic_data=None):
     """
     disable workspace notification
     """
     app_config = request.registry.settings["CFG"]  # type: CFG
     wapi = WorkspaceApi(
         current_user=request.candidate_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     workspace = wapi.get_one(hapic_data.path.workspace_id)
     wapi.disable_notifications(request.candidate_user, workspace)
     wapi.save(workspace)
Пример #19
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"]
Пример #20
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(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
Пример #21
0
 def guest_upload_file(self,
                       context,
                       request: TracimRequest,
                       hapic_data=None) -> None:
     """
     upload files as guest
     """
     # TODO - G.M - 2019-08-14 - replace UploadFiles Object hack to proper hapic support
     # see
     upload_files = UploadFiles(request, prefix_pattern="file_")
     # INFO - G.M - 2019-09-03 - check validation of file here, because hapic can't
     # handle them. verify if almost one file as been given.
     if len(upload_files.files) < 1:
         raise NoFileValidationError(
             "No files given at input, validation failed.")
     app_config = request.registry.settings["CFG"]  # type: CFG
     api = UploadPermissionLib(current_user=None,
                               session=request.dbsession,
                               config=app_config)
     upload_permission = api.get_upload_permission_by_token(
         upload_permission_token=hapic_data.path.upload_permission_token
     )  # type: UploadPermission
     # TODO - G.M - 2019-08-01 - verify in access to upload_permission can be granted
     # we should considered do these check at decorator level
     workspace_api = WorkspaceApi(current_user=None,
                                  session=request.dbsession,
                                  config=app_config)
     workspace = workspace_api.get_one(upload_permission.workspace_id)
     workspace_api.check_public_upload_enabled(workspace)
     api.check_password(upload_permission,
                        password=hapic_data.forms.password)
     content_api = ContentApi(current_user=None,
                              session=request.dbsession,
                              config=app_config)
     content_api.check_workspace_size_limitation(
         content_length=request.content_length, workspace=workspace)
     content_api.check_owner_size_limitation(
         content_length=request.content_length, workspace=workspace)
     api.upload_files(
         upload_permission=upload_permission,
         uploader_username=hapic_data.forms.username,
         message=hapic_data.forms.message,
         files=upload_files.files,
         do_notify=app_config.EMAIL__NOTIFICATION__ACTIVATED,
     )
     return
Пример #22
0
    def _create_role_event(self, operation: OperationType,
                           role: UserRoleInWorkspace,
                           context: TracimContext) -> None:
        current_user = context.safe_current_user()
        workspace_api = WorkspaceApi(
            session=context.dbsession,
            config=self._config,
            show_deleted=True,
            # INFO - G.M - 2020-17-09 - we do explicitly don't set user here to not
            # have filter on workspace, in some case we do want user create event on workspace
            # he doesn't have access: when he remove itself from workspace for example
            current_user=None,
        )
        workspace_in_context = workspace_api.get_workspace_with_context(
            workspace_api.get_one(role.workspace_id))
        user_api = UserApi(current_user,
                           context.dbsession,
                           self._config,
                           show_deleted=True)
        role_api = RoleApi(current_user=current_user,
                           session=context.dbsession,
                           config=self._config)
        try:
            user_field = EventApi.user_schema.dump(
                user_api.get_user_with_context(user_api.get_one(
                    role.user_id))).data
        except UserDoesNotExist:
            # It is possible to have an already deleted user when deleting his roles.
            user_field = None

        role_in_context = role_api.get_user_role_workspace_with_context(role)
        fields = {
            Event.USER_FIELD:
            user_field,
            Event.WORKSPACE_FIELD:
            EventApi.workspace_schema.dump(workspace_in_context).data,
            Event.MEMBER_FIELD:
            EventApi.workspace_user_role_schema.dump(role_in_context).data,
        }
        event_api = EventApi(current_user, context.dbsession, self._config)
        event_api.create_event(
            entity_type=EntityType.WORKSPACE_MEMBER,
            operation=operation,
            additional_fields=fields,
            context=context,
        )
Пример #23
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
Пример #24
0
    def _create_mention_events(
        cls, mentions: typing.Iterable[Mention], content: Content, context: TracimContext
    ) -> None:
        role_api = RoleApi(session=context.dbsession, config=context.app_config, current_user=None)

        workspace_members_usernames = [
            user.username for user in role_api.get_workspace_members(content.workspace_id)
        ]

        for mention in mentions:
            recipient = mention.recipient
            if (
                recipient not in workspace_members_usernames
                and recipient not in ALL__GROUP_MENTIONS
            ):
                raise UserNotMemberOfWorkspace(
                    "This user is not a member of this workspace: {}".format(mention.recipient)
                )

        current_user = context.safe_current_user()
        content_api = ContentApi(context.dbsession, current_user, context.app_config)
        content_in_context = content_api.get_content_in_context(content)
        workspace_api = WorkspaceApi(context.dbsession, current_user, context.app_config)
        workspace_in_context = workspace_api.get_workspace_with_context(
            workspace_api.get_one(content_in_context.workspace.workspace_id)
        )
        content_schema = EventApi.get_content_schema_for_type(content.type)
        content_dict = content_schema.dump(content_in_context).data
        common_fields = {
            Event.CONTENT_FIELD: content_dict,
            Event.WORKSPACE_FIELD: EventApi.workspace_schema.dump(workspace_in_context).data,
        }

        event_api = EventApi(current_user, context.dbsession, context.app_config)
        for mention in mentions:
            fields = {cls.MENTION_FIELD: {"recipient": mention.recipient, "id": mention.id}}
            fields.update(common_fields)
            event_api.create_event(
                entity_type=EntityType.MENTION,
                operation=OperationType.CREATED,
                additional_fields=fields,
                context=context,
            )
Пример #25
0
 def disable_workspace_notification(self, context, request: TracimRequest, hapic_data=None):  # nopep8
     """
     disable workspace notification
     """
     app_config = request.registry.settings['CFG']
     api = ContentApi(
         current_user=request.candidate_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     wapi = WorkspaceApi(
         current_user=request.candidate_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     workspace = wapi.get_one(hapic_data.path.workspace_id)
     wapi.disable_notifications(request.candidate_user, workspace)
     wapi.save(workspace)
     return
Пример #26
0
 def guest_upload_info(self,
                       context,
                       request: TracimRequest,
                       hapic_data=None) -> UploadPermissionInContext:
     """
     get somes informations about upload permission
     """
     app_config = request.registry.settings["CFG"]  # type: CFG
     api = UploadPermissionLib(current_user=None,
                               session=request.dbsession,
                               config=app_config)
     upload_permission = api.get_upload_permission_by_token(
         upload_permission_token=hapic_data.path.upload_permission_token
     )  # type: UploadPermission
     workspace_api = WorkspaceApi(current_user=None,
                                  session=request.dbsession,
                                  config=app_config)
     workspace = workspace_api.get_one(upload_permission.workspace_id)
     workspace_api.check_public_upload_enabled(workspace)
     return api.get_upload_permission_in_context(upload_permission)
Пример #27
0
 def disable_account_workspace_notification(self,
                                            context,
                                            request: TracimRequest,
                                            hapic_data=None):  # nopep8
     """
     disable workspace notification
     """
     app_config = request.registry.settings['CFG']
     api = ContentApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     wapi = WorkspaceApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     workspace = wapi.get_one(hapic_data.path.workspace_id)
     wapi.disable_notifications(request.current_user, workspace)
     wapi.save(workspace)
     return
Пример #28
0
 def enable_account_workspace_notification(self,
                                           context,
                                           request: TracimRequest,
                                           hapic_data=None):
     """
     enable workspace notification
     """
     app_config = request.registry.settings["CFG"]  # type: CFG
     wapi = WorkspaceApi(
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config  # User
     )
     workspace = wapi.get_one(hapic_data.path.workspace_id)
     wapi.enable_notifications(request.current_user, workspace)
     rapi = RoleApi(
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config  # User
     )
     rapi.get_one(request.current_user.user_id, workspace.workspace_id)
     wapi.save(workspace)
     return
Пример #29
0
 def _get_candidate_workspace(
         self,
         user: User,
         request: 'TracimRequest'
 ) -> Workspace:
     """
     Get current workspace from request
     :param user: User who want to check the workspace
     :param request: pyramid request
     :return: current workspace
     """
     workspace_id = ''
     try:
         if 'new_workspace_id' in request.json_body:
             workspace_id = request.json_body['new_workspace_id']
             if not isinstance(workspace_id, int):
                 if workspace_id.isdecimal():
                     workspace_id = int(workspace_id)
                 else:
                     raise InvalidWorkspaceId('workspace_id is not a correct integer')  # nopep8
         if not workspace_id:
             raise WorkspaceNotFoundInTracimRequest('No new_workspace_id property found in body')  # nopep8
         wapi = WorkspaceApi(
             current_user=user,
             session=request.dbsession,
             config=request.registry.settings['CFG'],
             show_deleted=True,
         )
         workspace = wapi.get_one(workspace_id)
     except JSONDecodeError as exc:
         raise WorkspaceNotFound('Invalid JSON content') from exc
     except NoResultFound as exc:
         raise WorkspaceNotFound(
             'Workspace {} does not exist '
             'or is not visible for this user'.format(workspace_id)
         ) from exc
     return workspace
Пример #30
0
 def guest_upload_check(self,
                        context,
                        request: TracimRequest,
                        hapic_data=None) -> None:
     """
     check upload password and token validity
     """
     app_config = request.registry.settings["CFG"]  # type: CFG
     api = UploadPermissionLib(current_user=None,
                               session=request.dbsession,
                               config=app_config)
     upload_permission = api.get_upload_permission_by_token(
         upload_permission_token=hapic_data.path.upload_permission_token
     )  # type: UploadPermission
     # TODO - G.M - 2019-08-01 - verify in access to upload_permission can be granted
     # we should considered do these check at decorator level
     workspace_api = WorkspaceApi(current_user=None,
                                  session=request.dbsession,
                                  config=app_config)
     workspace = workspace_api.get_one(upload_permission.workspace_id)
     workspace_api.check_public_upload_enabled(workspace)
     api.check_password(upload_permission,
                        password=hapic_data.body.password)
     return
Пример #31
0
 def delete_comment(self, context, request: TracimRequest, hapic_data=None):
     """
     Delete comment
     """
     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,
     )
     wapi = WorkspaceApi(
         current_user=request.current_user,
         session=request.dbsession,
         config=app_config,
     )
     workspace = wapi.get_one(hapic_data.path.workspace_id)
     parent = api.get_one(
         hapic_data.path.content_id,
         content_type=content_type_list.Any_SLUG,
         workspace=workspace
     )
     comment = api.get_one(
         hapic_data.path.comment_id,
         content_type=content_type_list.Comment.slug,
         workspace=workspace,
         parent=parent,
     )
     with new_revision(
             session=request.dbsession,
             tm=transaction.manager,
             content=comment
     ):
         api.delete(comment)
     return
Пример #32
0
    def notify_content_update(self, event_actor_id: int,
                              event_content_id: int) -> None:
        """
        Look for all users to be notified about the new content and send them an
        individual email
        :param event_actor_id: id of the user that has triggered the event
        :param event_content_id: related content_id
        :return:
        """
        # FIXME - D.A. - 2014-11-05
        # Dirty import. It's here in order to avoid circular import
        from tracim_backend.lib.core.content import ContentApi
        from tracim_backend.lib.core.user import UserApi

        user = UserApi(None, config=self.config,
                       session=self.session).get_one(event_actor_id)
        logger.debug(self, "Content: {}".format(event_content_id))
        content_api = ContentApi(current_user=user,
                                 session=self.session,
                                 config=self.config)
        content = ContentApi(
            session=self.session,
            current_user=user,
            # TODO - G.M - 2019-04-24 - use a system user instead of the user that has triggered the event
            config=self.config,
            show_archived=True,
            show_deleted=True,
        ).get_one(event_content_id, content_type_list.Any_SLUG)
        workspace_api = WorkspaceApi(session=self.session,
                                     current_user=user,
                                     config=self.config)
        workpace_in_context = workspace_api.get_workspace_with_context(
            workspace_api.get_one(content.workspace_id))
        main_content = content.parent if content.type == content_type_list.Comment.slug else content
        notifiable_roles = WorkspaceApi(
            current_user=user, session=self.session,
            config=self.config).get_notifiable_roles(content.workspace)

        if len(notifiable_roles) <= 0:
            logger.info(
                self,
                "Skipping notification as nobody subscribed to in workspace {}"
                .format(content.workspace.label),
            )
            return

        logger.info(
            self,
            "Generating content {} notification email for {} user(s)".format(
                content.content_id, len(notifiable_roles)),
        )
        # INFO - D.A. - 2014-11-06
        # The following email sender will send emails in the async task queue
        # This allow to build all mails through current thread but really send them (including SMTP connection)
        # In the other thread.
        #
        # This way, the webserver will return sooner (actually before notification emails are sent
        email_sender = EmailSender(self.config, self._smtp_config,
                                   self.config.EMAIL__NOTIFICATION__ACTIVATED)
        for role in notifiable_roles:
            logger.info(
                self,
                "Generating content {} notification email to {}".format(
                    content.content_id, role.user.email),
            )
            translator = Translator(app_config=self.config,
                                    default_lang=role.user.lang)
            _ = translator.get_translation
            # INFO - G.M - 2017-11-15 - set content_id in header to permit reply
            # references can have multiple values, but only one in this case.
            replyto_addr = self.config.EMAIL__NOTIFICATION__REPLY_TO__EMAIL.replace(
                "{content_id}", str(main_content.content_id))

            reference_addr = self.config.EMAIL__NOTIFICATION__REFERENCES__EMAIL.replace(
                "{content_id}", str(main_content.content_id))
            #
            #  INFO - D.A. - 2014-11-06
            # We do not use .format() here because the subject defined in the .ini file
            # may not include all required labels. In order to avoid partial format() (which result in an exception)
            # we do use replace and force the use of .__str__() in order to process LazyString objects
            #
            content_status = translator.get_translation(
                main_content.get_status().label)
            translated_subject = translator.get_translation(
                self.config.EMAIL__NOTIFICATION__CONTENT_UPDATE__SUBJECT)
            subject = translated_subject.replace(
                EST.WEBSITE_TITLE, self.config.WEBSITE__TITLE.__str__())
            subject = subject.replace(EST.WORKSPACE_LABEL,
                                      main_content.workspace.label.__str__())
            subject = subject.replace(EST.CONTENT_LABEL,
                                      main_content.label.__str__())
            subject = subject.replace(EST.CONTENT_STATUS_LABEL, content_status)
            reply_to_label = _(
                "{username} & all members of {workspace}").format(
                    username=user.display_name,
                    workspace=main_content.workspace.label)

            content_in_context = content_api.get_content_in_context(content)
            parent_in_context = None
            if content.parent_id:
                parent_in_context = content_api.get_content_in_context(
                    content.parent)

            body_html = self._build_email_body_for_content(
                self.config.
                EMAIL__NOTIFICATION__CONTENT_UPDATE__TEMPLATE__HTML,
                role,
                content_in_context,
                parent_in_context,
                workpace_in_context,
                user,
                translator,
            )

            message = EmailNotificationMessage(
                subject=subject,
                from_header=self._get_sender(user),
                to_header=EmailAddress(role.user.display_name,
                                       role.user.email),
                reply_to=EmailAddress(reply_to_label, replyto_addr),
                # INFO - G.M - 2017-11-15
                # References can theorically have label, but in pratice, references
                # contains only message_id from parents post in thread.
                # To link this email to a content we create a virtual parent
                # in reference who contain the content_id.
                # INFO - G.M - 2020-04-03 - Enforce angle bracket in references header
                # we need that to ensure best software compatibility
                # compat from parsing software
                references=EmailAddress("",
                                        reference_addr,
                                        force_angle_bracket=True),
                body_html=body_html,
                lang=translator.default_lang,
            )

            self.log_email_notification(
                msg="an email was created to {}".format(message["To"]),
                action="{:8s}".format("CREATED"),
                email_recipient=message["To"],
                email_subject=message["Subject"],
                config=self.config,
            )

            send_email_through(self.config, email_sender.send_mail, message)
Пример #33
0
    def notify_content_update(
            self,
            event_actor_id: int,
            event_content_id: int
    ) -> None:
        """
        Look for all users to be notified about the new content and send them an
        individual email
        :param event_actor_id: id of the user that has triggered the event
        :param event_content_id: related content_id
        :return:
        """
        # FIXME - D.A. - 2014-11-05
        # Dirty import. It's here in order to avoid circular import
        from tracim_backend.lib.core.content import ContentApi
        from tracim_backend.lib.core.user import UserApi
        user = UserApi(
            None,
            config=self.config,
            session=self.session,
            ).get_one(event_actor_id)
        logger.debug(self, 'Content: {}'.format(event_content_id))
        content_api = ContentApi(
            current_user=user,
            session=self.session,
            config=self.config,
            )
        content = ContentApi(
            session=self.session,
            current_user=user,  # nopep8 TODO - use a system user instead of the user that has triggered the event
            config=self.config,
            show_archived=True,
            show_deleted=True,
        ).get_one(event_content_id, content_type_list.Any_SLUG)
        workspace_api = WorkspaceApi(
            session=self.session,
            current_user=user,
            config=self.config,
        )
        workpace_in_context = workspace_api.get_workspace_with_context(workspace_api.get_one(content.workspace_id))  # nopep8
        main_content = content.parent if content.type == content_type_list.Comment.slug else content  # nopep8
        notifiable_roles = WorkspaceApi(
            current_user=user,
            session=self.session,
            config=self.config,
        ).get_notifiable_roles(content.workspace)

        if len(notifiable_roles) <= 0:
            logger.info(self, 'Skipping notification as nobody subscribed to in workspace {}'.format(content.workspace.label))
            return


        logger.info(self, 'Generating content {} notification email for {} user(s)'.format(
            content.content_id,
            len(notifiable_roles)
        ))
        # INFO - D.A. - 2014-11-06
        # The following email sender will send emails in the async task queue
        # This allow to build all mails through current thread but really send them (including SMTP connection)
        # In the other thread.
        #
        # This way, the webserver will return sooner (actually before notification emails are sent
        email_sender = EmailSender(
            self.config,
            self._smtp_config,
            self.config.EMAIL_NOTIFICATION_ACTIVATED
        )
        for role in notifiable_roles:
            logger.info(self,
                        'Generating content {} notification email to {}'.format(
                            content.content_id,
                            role.user.email
                        )
            )
            translator = Translator(app_config=self.config, default_lang=role.user.lang)  # nopep8
            _ = translator.get_translation
            to_addr = formataddr((role.user.display_name, role.user.email))
            # INFO - G.M - 2017-11-15 - set content_id in header to permit reply
            # references can have multiple values, but only one in this case.
            replyto_addr = self.config.EMAIL_NOTIFICATION_REPLY_TO_EMAIL.replace( # nopep8
                '{content_id}', str(main_content.content_id)
            )

            reference_addr = self.config.EMAIL_NOTIFICATION_REFERENCES_EMAIL.replace( #nopep8
                '{content_id}',str(main_content.content_id)
             )
            #
            #  INFO - D.A. - 2014-11-06
            # We do not use .format() here because the subject defined in the .ini file
            # may not include all required labels. In order to avoid partial format() (which result in an exception)
            # we do use replace and force the use of .__str__() in order to process LazyString objects
            #
            content_status = translator.get_translation(main_content.get_status().label)
            translated_subject = translator.get_translation(self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_SUBJECT)
            subject = translated_subject.replace(EST.WEBSITE_TITLE, self.config.WEBSITE_TITLE.__str__())
            subject = subject.replace(EST.WORKSPACE_LABEL, main_content.workspace.label.__str__())
            subject = subject.replace(EST.CONTENT_LABEL, main_content.label.__str__())
            subject = subject.replace(EST.CONTENT_STATUS_LABEL, content_status)
            reply_to_label = _('{username} & all members of {workspace}').format(  # nopep8
                username=user.display_name,
                workspace=main_content.workspace.label)

            message = MIMEMultipart('alternative')
            message['Subject'] = subject
            message['From'] = self._get_sender(user)
            message['To'] = to_addr
            message['Reply-to'] = formataddr((reply_to_label, replyto_addr))
            # INFO - G.M - 2017-11-15
            # References can theorically have label, but in pratice, references
            # contains only message_id from parents post in thread.
            # To link this email to a content we create a virtual parent
            # in reference who contain the content_id.
            message['References'] = formataddr(('', reference_addr))
            content_in_context = content_api.get_content_in_context(content)
            parent_in_context = None
            if content.parent_id:
                parent_in_context = content_api.get_content_in_context(content.parent) # nopep8

            body_html = self._build_email_body_for_content(
                self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML,
                role,
                content_in_context,
                parent_in_context,
                workpace_in_context,
                user,
                translator,
            )

            part2 = MIMEText(body_html, 'html', 'utf-8')
            # Attach parts into message container.
            # According to RFC 2046, the last part of a multipart message, in this case
            # the HTML message, is best and preferred.
            message.attach(part2)

            self.log_email_notification(
                msg='an email was created to {}'.format(message['To']),
                action='{:8s}'.format('CREATED'),
                email_recipient=message['To'],
                email_subject=message['Subject'],
                config=self.config,
            )

            send_email_through(
                self.config,
                email_sender.send_mail,
                message
            )
Пример #34
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'] == error.CONTENT_IN_NOT_EDITABLE_STATE
Пример #35
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']