Exemple #1
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'])
         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
 def delete_comment(self, context, request: TracimRequest, hapic_data=None):
     """
     Delete comment
     """
     app_config = request.registry.settings['CFG']
     api = ContentApi(
         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=ContentType.Any,
                          workspace=workspace)
     comment = api.get_one(
         hapic_data.path.comment_id,
         content_type=ContentType.Comment,
         workspace=workspace,
         parent=parent,
     )
     with new_revision(session=request.dbsession,
                       tm=transaction.manager,
                       content=comment):
         api.delete(comment)
     return
Exemple #3
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'])
         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
 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_datetime=None,
         content_ids=hapic_data.query.contents_ids or None)
     return [
         api.get_content_in_context(content) for content in last_actives
     ]
Exemple #5
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(
            ContentType.Folder,
            workspace,
            None,
            'parent',
            do_save=True,
            do_notify=False,
        )
        item2 = api.create(
            ContentType.File,
            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
Exemple #6
0
 def workspace(self, context, request: TracimRequest, hapic_data=None):
     """
     Get workspace informations
     """
     app_config = request.registry.settings['CFG']
     wapi = WorkspaceApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     return wapi.get_workspace_with_context(request.current_workspace)
Exemple #7
0
    def __init__(self, path: str, environ: dict, user: User, session: Session):
        super(RootResource, self).__init__(path, environ)

        self.user = user
        self.session = session
        # TODO BS 20170221: Web interface should list all workspace to. We
        # disable it here for moment. When web interface will be updated to
        # list all workspace, change this here to.
        self.workspace_api = WorkspaceApi(
            current_user=self.user,
            session=session,
            force_role=True,
            config=self.provider.app_config
        )
Exemple #8
0
 def test_unit__get_all_manageable(self):
     admin = self.session.query(User) \
         .filter(User.email == '*****@*****.**').one()
     uapi = UserApi(
         session=self.session,
         current_user=admin,
         config=self.config,
     )
     # Checks a case without workspaces.
     wapi = WorkspaceApi(
         session=self.session,
         current_user=admin,
         config=self.app_config,
     )
     eq_([], wapi.get_all_manageable())
     # Checks an admin gets all workspaces.
     w4 = wapi.create_workspace(label='w4')
     w3 = wapi.create_workspace(label='w3')
     w2 = wapi.create_workspace(label='w2')
     w1 = wapi.create_workspace(label='w1')
     eq_([w1, w2, w3, w4], wapi.get_all_manageable())
     # Checks a regular user gets none workspace.
     gapi = GroupApi(
         session=self.session,
         current_user=None,
         config=self.app_config,
     )
     u = uapi.create_minimal_user('[email protected]', [gapi.get_one(Group.TIM_USER)], True)
     wapi = WorkspaceApi(
         session=self.session,
         current_user=u,
         config=self.app_config,
     )
     rapi = RoleApi(
         session=self.session,
         current_user=u,
         config=self.app_config,
     )
     rapi.create_one(u, w4, UserRoleInWorkspace.READER, False)
     rapi.create_one(u, w3, UserRoleInWorkspace.CONTRIBUTOR, False)
     rapi.create_one(u, w2, UserRoleInWorkspace.CONTENT_MANAGER, False)
     rapi.create_one(u, w1, UserRoleInWorkspace.WORKSPACE_MANAGER, False)
     eq_([], wapi.get_all_manageable())
     # Checks a manager gets only its own workspaces.
     u.groups.append(gapi.get_one(Group.TIM_MANAGER))
     rapi.delete_one(u.user_id, w2.workspace_id)
     rapi.create_one(u, w2, UserRoleInWorkspace.WORKSPACE_MANAGER, False)
     eq_([w1, w2], wapi.get_all_manageable())
Exemple #9
0
 def create_workspace(self, context, request: TracimRequest, hapic_data=None):  # nopep8
     """
     create workspace
     """
     app_config = request.registry.settings['CFG']
     wapi = WorkspaceApi(
         current_user=request.current_user,  # User
         session=request.dbsession,
         config=app_config,
     )
     workspace = wapi.create_workspace(
         label=hapic_data.body.label,
         description=hapic_data.body.description,
         save_now=True,
     )
     return wapi.get_workspace_with_context(workspace)
    def user_workspace(self, context, request: TracimRequest, hapic_data=None):
        """
        Get list of user workspaces
        """
        app_config = request.registry.settings['CFG']
        wapi = WorkspaceApi(
            current_user=request.candidate_user,  # User
            session=request.dbsession,
            config=app_config,
        )

        workspaces = wapi.get_all_for_user(request.candidate_user)
        return [
            wapi.get_workspace_with_context(workspace)
            for workspace in workspaces
        ]
Exemple #11
0
    def move_folder(self, destpath):

        workspace_api = WorkspaceApi(
            current_user=self.user,
            session=self.session,
            config=self.provider.app_config,
        )
        workspace = self.provider.get_workspace_from_path(
            normpath(destpath), workspace_api
        )

        parent = self.provider.get_parent_from_path(
            normpath(destpath),
            self.content_api,
            workspace
        )

        with new_revision(
            content=self.content,
            tm=transaction.manager,
            session=self.session,
        ):
            if basename(destpath) != self.getDisplayName():
                self.content_api.update_content(self.content, transform_to_bdd(basename(destpath)))
                self.content_api.save(self.content)
            else:
                if workspace.workspace_id == self.content.workspace.workspace_id:
                    self.content_api.move(self.content, parent)
                else:
                    self.content_api.move_recursively(self.content, parent, workspace)

        transaction.commit()
Exemple #12
0
 def test_get_notifiable_roles(self):
     admin = self.session.query(User) \
         .filter(User.email == '*****@*****.**').one()
     wapi = WorkspaceApi(
         session=self.session,
         config=self.app_config,
         current_user=admin,
     )
     w = wapi.create_workspace(label='workspace w', save_now=True)
     uapi = UserApi(
         session=self.session,
         current_user=admin,
         config=self.config
     )
     u = uapi.create_minimal_user(email='[email protected]', save_now=True)
     eq_([], wapi.get_notifiable_roles(workspace=w))
     rapi = RoleApi(
         session=self.session,
         current_user=admin,
         config=self.app_config,
     )
     r = rapi.create_one(u, w, UserRoleInWorkspace.READER, with_notif=True)
     eq_([r, ], wapi.get_notifiable_roles(workspace=w))
     u.is_active = False
     eq_([], wapi.get_notifiable_roles(workspace=w))
Exemple #13
0
def get_workspace(user: User, request: Request) -> typing.Optional[Workspace]:
    """
    Get current workspace from request
    :param user: User who want to check the workspace
    :param request: pyramid request
    :return:
    """
    workspace_id = ''
    try:
        if 'workspace_id' not in request.json_body:
            return None
        workspace_id = request.json_body['workspace_id']
        wapi = WorkspaceApi(current_user=user, session=request.dbsession)
        workspace = wapi.get_one(workspace_id)
    except JSONDecodeError:
        raise WorkspaceNotFound('Bad json body')
    except NoResultFound:
        raise WorkspaceNotFound(
            'Workspace {} does not exist '
            'or is not visible for this user'.format(workspace_id)
        )
    return workspace
Exemple #14
0
    def exists(self, path, environ) -> bool:
        """
        Called by wsgidav to check if a certain path is linked to a _DAVResource
        """

        path = normpath(path)
        working_path = self.reduce_path(path)
        root_path = environ['http_authenticator.realm']
        parent_path = dirname(working_path)
        user = environ['tracim_user']
        session = environ['tracim_dbsession']
        if path == root_path:
            return True

        workspace = self.get_workspace_from_path(
            path,
            WorkspaceApi(
                current_user=user,
                session=session,
                config=self.app_config,
            )
        )

        if parent_path == root_path or workspace is None:
            return workspace is not None

        # TODO bastien: Arnaud avait mis a True, verif le comportement
        # lorsque l'on explore les dossiers archive et deleted
        content_api = ContentApi(
            current_user=user,
            session=session,
            config=self.app_config,
            show_archived=False,
            show_deleted=False
        )

        revision_id = re.search(r'/\.history/[^/]+/\((\d+) - [a-zA-Z]+\) ([^/].+)$', path)

        is_archived = self.is_path_archive(path)

        is_deleted = self.is_path_delete(path)

        if revision_id:
            revision_id = revision_id.group(1)
            content = content_api.get_one_revision(revision_id)
        else:
            content = self.get_content_from_path(working_path, content_api, workspace)

        return content is not None \
            and content.is_deleted == is_deleted \
            and content.is_archived == is_archived
Exemple #15
0
    def copyMoveSingle(self, destpath, isMove):
        if isMove:
            # INFO - G.M - 12-03-2018 - This case should not happen
            # As far as moveRecursive method exist, all move should not go
            # through this method. If such case appear, try replace this to :
            ####
            # self.move_file(destpath)
            # return
            ####

            raise NotImplemented

        new_file_name = None
        new_file_extension = None

        # Inspect destpath
        if basename(destpath) != self.getDisplayName():
            new_given_file_name = transform_to_bdd(basename(destpath))
            new_file_name, new_file_extension = \
                os.path.splitext(new_given_file_name)

        workspace_api = WorkspaceApi(
            current_user=self.user,
            session=self.session,
            config=self.provider.app_config,
        )
        content_api = ContentApi(
            current_user=self.user,
            session=self.session,
            config=self.provider.app_config
        )
        destination_workspace = self.provider.get_workspace_from_path(
            destpath,
            workspace_api,
        )
        destination_parent = self.provider.get_parent_from_path(
            destpath,
            content_api,
            destination_workspace,
        )
        workspace = self.content.workspace
        parent = self.content.parent
        new_content = self.content_api.copy(
            item=self.content,
            new_label=new_file_name,
            new_parent=destination_parent,
        )
        self.content_api.copy_children(self.content, new_content)
        transaction.commit()
Exemple #16
0
    def _create_workspace_and_test(self, name, user) -> Workspace:
        """
        All extra parameters (*args, **kwargs) are for Workspace init
        :return: Created workspace instance
        """
        WorkspaceApi(
            current_user=user,
            session=self.session,
        ).create_workspace(name, save_now=True)

        eq_(
            1,
            self.session.query(Workspace).filter(
                Workspace.label == name).count())
        return self.session.query(Workspace).filter(
            Workspace.label == name).one()
Exemple #17
0
 def get_workspace_from_path(self, path: str, api: WorkspaceApi) -> Workspace:
     try:
         return api.get_one_by_label(transform_to_bdd(path.split('/')[1]))
     except NoResultFound:
         return None
Exemple #18
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.lib.core.content import ContentApi
        from tracim.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 - 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, ContentType.Any)
        main_content = content.parent if content.type == ContentType.Comment 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, 'Sending asynchronous emails to {} user(s)'.format(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
        async_email_sender = EmailSender(
            self.config,
            self._smtp_config,
            self.config.EMAIL_NOTIFICATION_ACTIVATED
        )
        for role in notifiable_roles:
            logger.info(self, 'Sending email to {}'.format(role.user.email))
            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(content.content_id)
            )

            reference_addr = self.config.EMAIL_NOTIFICATION_REFERENCES_EMAIL.replace( #nopep8
                '{content_id}',str(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
            #
            subject = self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_SUBJECT
            subject = 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, main_content.get_status().label.__str__())
            reply_to_label = l_('{username} & all members of {workspace}').format(
                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))
            body_text = self._build_email_body_for_content(self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_TEXT, role, content, user)
            body_html = self._build_email_body_for_content(self.config.EMAIL_NOTIFICATION_CONTENT_UPDATE_TEMPLATE_HTML, role, content, user)

            part1 = MIMEText(body_text, 'plain', 'utf-8')
            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(part1)
            message.attach(part2)

            self.log_notification(
                action='CREATED',
                recipient=message['To'],
                subject=message['Subject'],
                config=self.config,
            )

            send_email_through(
                self.config,
                async_email_sender.send_mail,
                message
            )
Exemple #19
0
    def insert(self):
        admin = self._session.query(models.User) \
            .filter(models.User.email == '*****@*****.**') \
            .one()
        bob = self._session.query(models.User) \
            .filter(models.User.email == '*****@*****.**') \
            .one()
        john_the_reader = self._session.query(models.User) \
            .filter(models.User.email == '*****@*****.**') \
            .one()

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

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

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

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

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

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

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

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

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

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

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

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

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

        with new_revision(
                session=self._session,
                tm=transaction.manager,
                content=tiramisu_page,
        ):
            bob_content_api.update_content(
                item=tiramisu_page,
                new_content=
                '<p>To cook a great Tiramisu, you need many ingredients.</p>',  # nopep8
                new_label='Tiramisu Recipe',
            )
            bob_content_api.save(tiramisu_page)
        self._session.flush()
Exemple #20
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()
        admin_workspace_api = WorkspaceApi(
            current_user=admin,
            session=self._session,
        )
        bob_workspace_api = WorkspaceApi(
            current_user=bob,
            session=self._session,
        )
        content_api = ContentApi(
            current_user=admin,
            session=self._session,
        )
        role_api = RoleApi(
            current_user=admin,
            session=self._session,
        )

        # Workspaces
        w1 = admin_workspace_api.create_workspace('w1', save_now=True)
        w2 = bob_workspace_api.create_workspace('w2', save_now=True)
        w3 = admin_workspace_api.create_workspace('w3', save_now=True)

        # Workspaces roles
        role_api.create_one(
            user=bob,
            workspace=w1,
            role_level=UserRoleInWorkspace.CONTENT_MANAGER,
            with_notif=False,
        )

        # Folders
        w1f1 = content_api.create(
            content_type=ContentType.Folder,
            workspace=w1,
            label='w1f1',
            do_save=True,
        )
        w1f2 = content_api.create(
            content_type=ContentType.Folder,
            workspace=w1,
            label='w1f2',
            do_save=True,
        )

        w2f1 = content_api.create(
            content_type=ContentType.Folder,
            workspace=w2,
            label='w2f1',
            do_save=True,
        )
        w2f2 = content_api.create(
            content_type=ContentType.Folder,
            workspace=w2,
            label='w2f2',
            do_save=True,
        )

        w3f1 = content_api.create(
            content_type=ContentType.Folder,
            workspace=w3,
            label='w3f3',
            do_save=True,
        )

        # Pages, threads, ..
        w1f1p1 = content_api.create(
            content_type=ContentType.Page,
            workspace=w1,
            parent=w1f1,
            label='w1f1p1',
            do_save=True,
        )
        w1f1t1 = content_api.create(
            content_type=ContentType.Thread,
            workspace=w1,
            parent=w1f1,
            label='w1f1t1',
            do_save=False,
        )
        w1f1t1.description = 'w1f1t1 description'
        self._session.add(w1f1t1)
        w1f1d1_txt = content_api.create(
            content_type=ContentType.File,
            workspace=w1,
            parent=w1f1,
            label='w1f1d1',
            do_save=False,
        )
        w1f1d1_txt.file_extension = '.txt'
        w1f1d1_txt.depot_file = FileIntent(
            b'w1f1d1 content',
            'w1f1d1.txt',
            'text/plain',
        )
        self._session.add(w1f1d1_txt)
        w1f1d2_html = content_api.create(
            content_type=ContentType.File,
            workspace=w1,
            parent=w1f1,
            label='w1f1d2',
            do_save=False,
        )
        w1f1d2_html.file_extension = '.html'
        w1f1d2_html.depot_file = FileIntent(
            b'<p>w1f1d2 content</p>',
            'w1f1d2.html',
            'text/html',
        )
        self._session.add(w1f1d2_html)
        w1f1f1 = content_api.create(
            content_type=ContentType.Folder,
            workspace=w1,
            label='w1f1f1',
            parent=w1f1,
            do_save=True,
        )

        w2f1p1 = content_api.create(
            content_type=ContentType.Page,
            workspace=w2,
            parent=w2f1,
            label='w2f1p1',
            do_save=True,
        )
        self._session.flush()
Exemple #21
0
    def move_file(self, destpath: str) -> None:
        """
        Move file mean changing the path to access to a file. This can mean
        simple renaming(1), moving file from a directory to one another(2)
        but also renaming + moving file from a directory to one another at
        the same time (3).

        (1): move /dir1/file1 -> /dir1/file2
        (2): move /dir1/file1 -> /dir2/file1
        (3): move /dir1/file1 -> /dir2/file2
        :param destpath: destination path of webdav move
        :return: nothing
        """

        workspace = self.content.workspace
        parent = self.content.parent

        with new_revision(
            content=self.content,
            tm=transaction.manager,
            session=self.session,
        ):
            # INFO - G.M - 2018-03-09 - First, renaming file if needed
            if basename(destpath) != self.getDisplayName():
                new_given_file_name = transform_to_bdd(basename(destpath))
                new_file_name, new_file_extension = \
                    os.path.splitext(new_given_file_name)

                self.content_api.update_content(
                    self.content,
                    new_file_name,
                )
                self.content.file_extension = new_file_extension
                self.content_api.save(self.content)

            # INFO - G.M - 2018-03-09 - Moving file if needed
            workspace_api = WorkspaceApi(
                current_user=self.user,
                session=self.session,
                config=self.provider.app_config,
                )
            content_api = ContentApi(
                current_user=self.user,
                session=self.session,
                config=self.provider.app_config
            )

            destination_workspace = self.provider.get_workspace_from_path(
                destpath,
                workspace_api,
            )
            destination_parent = self.provider.get_parent_from_path(
                destpath,
                content_api,
                destination_workspace,
            )
            if destination_parent != parent or destination_workspace != workspace:  # nopep8
                #  INFO - G.M - 12-03-2018 - Avoid moving the file "at the same place"  # nopep8
                #  if the request does not result in a real move.
                self.content_api.move(
                    item=self.content,
                    new_parent=destination_parent,
                    must_stay_in_same_workspace=False,
                    new_workspace=destination_workspace
                )

        transaction.commit()
Exemple #22
0
    def getResourceInst(self, path: str, environ: dict):
        """
        Called by wsgidav whenever a request is called to get the _DAVResource corresponding to the path
        """
        user = environ['tracim_user']
        session = environ['tracim_dbsession']
        if not self.exists(path, environ):
            return None
        path = normpath(path)
        root_path = environ['http_authenticator.realm']

        # If the requested path is the root, then we return a RootResource resource
        if path == root_path:
            return resources.RootResource(
                path=path,
                environ=environ,
                user=user,
                session=session
            )

        workspace_api = WorkspaceApi(
            current_user=user,
            session=session,
            config=self.app_config,
        )
        workspace = self.get_workspace_from_path(path, workspace_api)

        # If the request path is in the form root/name, then we return a WorkspaceResource resource
        parent_path = dirname(path)
        if parent_path == root_path:
            if not workspace:
                return None
            return resources.WorkspaceResource(
                path=path,
                environ=environ,
                workspace=workspace,
                user=user,
                session=session,
            )

        # And now we'll work on the path to establish which type or resource is requested

        content_api = ContentApi(
            current_user=user,
            session=session,
            config=self.app_config,
            show_archived=False,  # self._show_archive,
            show_deleted=False,  # self._show_delete
        )

        content = self.get_content_from_path(
            path=path,
            content_api=content_api,
            workspace=workspace
        )


        # Easy cases : path either end with /.deleted, /.archived or /.history, then we return corresponding resources
        if path.endswith(SpecialFolderExtension.Archived) and self._show_archive:
            return resources.ArchivedFolderResource(
                path=path,
                environ=environ,
                workspace=workspace,
                user=user,
                content=content,
                session=session,
            )

        if path.endswith(SpecialFolderExtension.Deleted) and self._show_delete:
            return resources.DeletedFolderResource(
                path=path,
                environ=environ,
                workspace=workspace,
                user=user,
                content=content,
                session=session,
            )

        if path.endswith(SpecialFolderExtension.History) and self._show_history:
            is_deleted_folder = re.search(r'/\.deleted/\.history$', path) is not None
            is_archived_folder = re.search(r'/\.archived/\.history$', path) is not None

            type = HistoryType.Deleted if is_deleted_folder \
                else HistoryType.Archived if is_archived_folder \
                else HistoryType.Standard

            return resources.HistoryFolderResource(
                path=path,
                environ=environ,
                workspace=workspace,
                user=user,
                content=content,
                session=session,
                type=type
            )

        # Now that's more complicated, we're trying to find out if the path end with /.history/file_name
        is_history_file_folder = re.search(r'/\.history/([^/]+)$', path) is not None

        if is_history_file_folder and self._show_history:
            return resources.HistoryFileFolderResource(
                path=path,
                environ=environ,
                user=user,
                content=content,
                session=session,
            )
        # And here next step :
        is_history_file = re.search(r'/\.history/[^/]+/\((\d+) - [a-zA-Z]+\) .+', path) is not None

        if self._show_history and is_history_file:

            revision_id = re.search(r'/\.history/[^/]+/\((\d+) - [a-zA-Z]+\) ([^/].+)$', path).group(1)

            content_revision = content_api.get_one_revision(revision_id)
            content = self.get_content_from_revision(content_revision, content_api)

            if content.type == ContentType.File:
                return resources.HistoryFileResource(
                    path=path,
                    environ=environ,
                    user=user,
                    content=content,
                    content_revision=content_revision,
                    session=session,
                )
            else:
                return resources.HistoryOtherFile(
                    path=path,
                    environ=environ,
                    user=user,
                    content=content,
                    content_revision=content_revision,
                    session=session,
                )

        # And if we're still going, the client is asking for a standard Folder/File/Page/Thread so we check the type7
        # and return the corresponding resource

        if content is None:
            return None
        if content.type == ContentType.Folder:
            return resources.FolderResource(
                path=path,
                environ=environ,
                workspace=content.workspace,
                content=content,
                session=session,
                user=user,
            )
        elif content.type == ContentType.File:
            return resources.FileResource(
                path=path,
                environ=environ,
                content=content,
                session=session,
                user=user
            )
        else:
            return resources.OtherFileResource(
                path=path,
                environ=environ,
                content=content,
                session=session,
                user=user,
            )
Exemple #23
0
class RootResource(DAVCollection):
    """
    RootResource ressource that represents tracim's home, which contains all workspaces
    """

    def __init__(self, path: str, environ: dict, user: User, session: Session):
        super(RootResource, self).__init__(path, environ)

        self.user = user
        self.session = session
        # TODO BS 20170221: Web interface should list all workspace to. We
        # disable it here for moment. When web interface will be updated to
        # list all workspace, change this here to.
        self.workspace_api = WorkspaceApi(
            current_user=self.user,
            session=session,
            force_role=True,
            config=self.provider.app_config
        )

    def __repr__(self) -> str:
        return '<DAVCollection: RootResource>'

    def getMemberNames(self) -> [str]:
        """
        This method returns the names (here workspace's labels) of all its children

        Though for perfomance issue, we're not using this function anymore
        """
        return [workspace.label for workspace in self.workspace_api.get_all()]

    def getMember(self, label: str) -> DAVCollection:
        """
        This method returns the child Workspace that corresponds to a given name

        Though for perfomance issue, we're not using this function anymore
        """
        try:
            workspace = self.workspace_api.get_one_by_label(label)
            workspace_path = '%s%s%s' % (self.path, '' if self.path == '/' else '/', transform_to_display(workspace.label))

            return WorkspaceResource(
                workspace_path,
                self.environ,
                workspace,
                session=self.session,
                user=self.user,
            )
        except AttributeError:
            return None

    def createEmptyResource(self, name: str):
        """
        This method is called whenever the user wants to create a DAVNonCollection resource (files in our case).

        There we don't allow to create files at the root;
        only workspaces (thus collection) can be created.
        """
        raise DAVError(HTTP_FORBIDDEN)

    def createCollection(self, name: str):
        """
        This method is called whenever the user wants to create a DAVCollection resource as a child (in our case,
        we create workspaces as this is the root).

        [For now] we don't allow to create new workspaces through
        webdav client. Though if we come to allow it, deleting the error's raise will
        make it possible.
        """
        # TODO : remove comment here
        # raise DAVError(HTTP_FORBIDDEN)

        new_workspace = self.workspace_api.create_workspace(name)
        self.workspace_api.save(new_workspace)

        workspace_path = '%s%s%s' % (
        self.path, '' if self.path == '/' else '/', transform_to_display(new_workspace.label))

        transaction.commit()
        return WorkspaceResource(
            workspace_path,
            self.environ,
            new_workspace,
            user=self.user,
            session=self.session,
        )

    def getMemberList(self):
        """
        This method is called by wsgidav when requesting with a depth > 0, it will return a list of _DAVResource
        of all its direct children
        """

        members = []
        for workspace in self.workspace_api.get_all():
            workspace_path = '%s%s%s' % (self.path, '' if self.path == '/' else '/', workspace.label)
            members.append(
                WorkspaceResource(
                    path=workspace_path,
                    environ=self.environ,
                    workspace=workspace,
                    user=self.user,
                    session=self.session,
                )
            )

        return members