|  | @@ -343,56 +343,57 @@ class ContentApi(object):
 | 
	
		
			
			| 343 | 343 |      ) -> Query:
 | 
	
		
			
			| 344 | 344 |          return self._base_query(workspace)
 | 
	
		
			
			| 345 | 345 |  
 | 
	
		
			
			| 346 |  | -    def get_child_folders(self, parent: Content=None, workspace: Workspace=None, filter_by_allowed_content_types: list=[], removed_item_ids: list=[], allowed_node_types=None) -> typing.List[Content]:
 | 
	
		
			
			| 347 |  | -        """
 | 
	
		
			
			| 348 |  | -        This method returns child items (folders or items) for left bar treeview.
 | 
	
		
			
			| 349 |  | -
 | 
	
		
			
			| 350 |  | -        :param parent:
 | 
	
		
			
			| 351 |  | -        :param workspace:
 | 
	
		
			
			| 352 |  | -        :param filter_by_allowed_content_types:
 | 
	
		
			
			| 353 |  | -        :param removed_item_ids:
 | 
	
		
			
			| 354 |  | -        :param allowed_node_types: This parameter allow to hide folders for which the given type of content is not allowed.
 | 
	
		
			
			| 355 |  | -               For example, if you want to move a Page from a folder to another, you should show only folders that accept pages
 | 
	
		
			
			| 356 |  | -        :return:
 | 
	
		
			
			| 357 |  | -        """
 | 
	
		
			
			| 358 |  | -        filter_by_allowed_content_types = filter_by_allowed_content_types or []  # FDV
 | 
	
		
			
			| 359 |  | -        removed_item_ids = removed_item_ids or []  # FDV
 | 
	
		
			
			| 360 |  | -
 | 
	
		
			
			| 361 |  | -        if not allowed_node_types:
 | 
	
		
			
			| 362 |  | -            allowed_node_types = [ContentType.Folder]
 | 
	
		
			
			| 363 |  | -        elif allowed_node_types==ContentType.Any:
 | 
	
		
			
			| 364 |  | -            allowed_node_types = ContentType.all()
 | 
	
		
			
			| 365 |  | -
 | 
	
		
			
			| 366 |  | -        parent_id = parent.content_id if parent else None
 | 
	
		
			
			| 367 |  | -        folders = self._base_query(workspace).\
 | 
	
		
			
			| 368 |  | -            filter(Content.parent_id==parent_id).\
 | 
	
		
			
			| 369 |  | -            filter(Content.type.in_(allowed_node_types)).\
 | 
	
		
			
			| 370 |  | -            filter(Content.content_id.notin_(removed_item_ids)).\
 | 
	
		
			
			| 371 |  | -            all()
 | 
	
		
			
			| 372 |  | -
 | 
	
		
			
			| 373 |  | -        if not filter_by_allowed_content_types or \
 | 
	
		
			
			| 374 |  | -                        len(filter_by_allowed_content_types)<=0:
 | 
	
		
			
			| 375 |  | -            # Standard case for the left treeview: we want to show all contents
 | 
	
		
			
			| 376 |  | -            # in the left treeview... so we still filter because for example
 | 
	
		
			
			| 377 |  | -            # comments must not appear in the treeview
 | 
	
		
			
			| 378 |  | -            return [folder for folder in folders \
 | 
	
		
			
			| 379 |  | -                    if folder.type in ContentType.allowed_types_for_folding()]
 | 
	
		
			
			| 380 |  | -
 | 
	
		
			
			| 381 |  | -        # Now this is a case of Folders only (used for moving content)
 | 
	
		
			
			| 382 |  | -        # When moving a content, you must get only folders that allow to be filled
 | 
	
		
			
			| 383 |  | -        # with the type of content you want to move
 | 
	
		
			
			| 384 |  | -        result = []
 | 
	
		
			
			| 385 |  | -        for folder in folders:
 | 
	
		
			
			| 386 |  | -            for allowed_content_type in filter_by_allowed_content_types:
 | 
	
		
			
			| 387 |  | -
 | 
	
		
			
			| 388 |  | -                is_folder = folder.type == ContentType.Folder
 | 
	
		
			
			| 389 |  | -                content_type__allowed = folder.properties['allowed_content'][allowed_content_type] == True
 | 
	
		
			
			| 390 |  | -
 | 
	
		
			
			| 391 |  | -                if is_folder and content_type__allowed:
 | 
	
		
			
			| 392 |  | -                    result.append(folder)
 | 
	
		
			
			| 393 |  | -                    break
 | 
	
		
			
			| 394 |  | -
 | 
	
		
			
			| 395 |  | -        return result
 | 
	
		
			
			|  | 346 | +    # TODO - G.M - 2018-07-17 - [Cleanup] Drop this method if unneeded
 | 
	
		
			
			|  | 347 | +    # def get_child_folders(self, parent: Content=None, workspace: Workspace=None, filter_by_allowed_content_types: list=[], removed_item_ids: list=[], allowed_node_types=None) -> typing.List[Content]:
 | 
	
		
			
			|  | 348 | +    #     """
 | 
	
		
			
			|  | 349 | +    #     This method returns child items (folders or items) for left bar treeview.
 | 
	
		
			
			|  | 350 | +    # 
 | 
	
		
			
			|  | 351 | +    #     :param parent:
 | 
	
		
			
			|  | 352 | +    #     :param workspace:
 | 
	
		
			
			|  | 353 | +    #     :param filter_by_allowed_content_types:
 | 
	
		
			
			|  | 354 | +    #     :param removed_item_ids:
 | 
	
		
			
			|  | 355 | +    #     :param allowed_node_types: This parameter allow to hide folders for which the given type of content is not allowed.
 | 
	
		
			
			|  | 356 | +    #            For example, if you want to move a Page from a folder to another, you should show only folders that accept pages
 | 
	
		
			
			|  | 357 | +    #     :return:
 | 
	
		
			
			|  | 358 | +    #     """
 | 
	
		
			
			|  | 359 | +    #     filter_by_allowed_content_types = filter_by_allowed_content_types or []  # FDV
 | 
	
		
			
			|  | 360 | +    #     removed_item_ids = removed_item_ids or []  # FDV
 | 
	
		
			
			|  | 361 | +    # 
 | 
	
		
			
			|  | 362 | +    #     if not allowed_node_types:
 | 
	
		
			
			|  | 363 | +    #         allowed_node_types = [ContentType.Folder]
 | 
	
		
			
			|  | 364 | +    #     elif allowed_node_types==ContentType.Any:
 | 
	
		
			
			|  | 365 | +    #         allowed_node_types = ContentType.all()
 | 
	
		
			
			|  | 366 | +    # 
 | 
	
		
			
			|  | 367 | +    #     parent_id = parent.content_id if parent else None
 | 
	
		
			
			|  | 368 | +    #     folders = self._base_query(workspace).\
 | 
	
		
			
			|  | 369 | +    #         filter(Content.parent_id==parent_id).\
 | 
	
		
			
			|  | 370 | +    #         filter(Content.type.in_(allowed_node_types)).\
 | 
	
		
			
			|  | 371 | +    #         filter(Content.content_id.notin_(removed_item_ids)).\
 | 
	
		
			
			|  | 372 | +    #         all()
 | 
	
		
			
			|  | 373 | +    # 
 | 
	
		
			
			|  | 374 | +    #     if not filter_by_allowed_content_types or \
 | 
	
		
			
			|  | 375 | +    #                     len(filter_by_allowed_content_types)<=0:
 | 
	
		
			
			|  | 376 | +    #         # Standard case for the left treeview: we want to show all contents
 | 
	
		
			
			|  | 377 | +    #         # in the left treeview... so we still filter because for example
 | 
	
		
			
			|  | 378 | +    #         # comments must not appear in the treeview
 | 
	
		
			
			|  | 379 | +    #         return [folder for folder in folders \
 | 
	
		
			
			|  | 380 | +    #                 if folder.type in ContentType.allowed_types_for_folding()]
 | 
	
		
			
			|  | 381 | +    # 
 | 
	
		
			
			|  | 382 | +    #     # Now this is a case of Folders only (used for moving content)
 | 
	
		
			
			|  | 383 | +    #     # When moving a content, you must get only folders that allow to be filled
 | 
	
		
			
			|  | 384 | +    #     # with the type of content you want to move
 | 
	
		
			
			|  | 385 | +    #     result = []
 | 
	
		
			
			|  | 386 | +    #     for folder in folders:
 | 
	
		
			
			|  | 387 | +    #         for allowed_content_type in filter_by_allowed_content_types:
 | 
	
		
			
			|  | 388 | +    # 
 | 
	
		
			
			|  | 389 | +    #             is_folder = folder.type == ContentType.Folder
 | 
	
		
			
			|  | 390 | +    #             content_type__allowed = folder.properties['allowed_content'][allowed_content_type] == True
 | 
	
		
			
			|  | 391 | +    # 
 | 
	
		
			
			|  | 392 | +    #             if is_folder and content_type__allowed:
 | 
	
		
			
			|  | 393 | +    #                 result.append(folder)
 | 
	
		
			
			|  | 394 | +    #                 break
 | 
	
		
			
			|  | 395 | +    # 
 | 
	
		
			
			|  | 396 | +    #     return result
 | 
	
		
			
			| 396 | 397 |  
 | 
	
		
			
			| 397 | 398 |      def create(self, content_type: str, workspace: Workspace, parent: Content=None, label: str ='', filename: str = '', do_save=False, is_temporary: bool=False, do_notify=True) -> Content:
 | 
	
		
			
			| 398 | 399 |          # TODO - G.M - 2018-07-16 - raise Exception instead of assert
 | 
	
	
		
			
			|  | @@ -724,10 +725,21 @@ class ContentApi(object):
 | 
	
		
			
			| 724 | 725 |              ),
 | 
	
		
			
			| 725 | 726 |          ))
 | 
	
		
			
			| 726 | 727 |  
 | 
	
		
			
			| 727 |  | -    def get_all(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]:
 | 
	
		
			
			| 728 |  | -        assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
 | 
	
		
			
			| 729 |  | -        if not content_type:
 | 
	
		
			
			| 730 |  | -            content_type = ContentType.Any
 | 
	
		
			
			|  | 728 | +    def _get_all_query(
 | 
	
		
			
			|  | 729 | +        self,
 | 
	
		
			
			|  | 730 | +        parent_id: int = None,
 | 
	
		
			
			|  | 731 | +        content_type: str = ContentType.Any,
 | 
	
		
			
			|  | 732 | +        workspace: Workspace = None
 | 
	
		
			
			|  | 733 | +    ) -> Query:
 | 
	
		
			
			|  | 734 | +        """
 | 
	
		
			
			|  | 735 | +        Extended filter for better "get all data" query
 | 
	
		
			
			|  | 736 | +        :param parent_id: filter by parent_id
 | 
	
		
			
			|  | 737 | +        :param content_type: filter by content_type slug
 | 
	
		
			
			|  | 738 | +        :param workspace: filter by workspace
 | 
	
		
			
			|  | 739 | +        :return:
 | 
	
		
			
			|  | 740 | +        """
 | 
	
		
			
			|  | 741 | +        assert parent_id is None or isinstance(parent_id, int)
 | 
	
		
			
			|  | 742 | +        assert content_type is not None
 | 
	
		
			
			| 731 | 743 |          resultset = self._base_query(workspace)
 | 
	
		
			
			| 732 | 744 |  
 | 
	
		
			
			| 733 | 745 |          if content_type!=ContentType.Any:
 | 
	
	
		
			
			|  | @@ -740,28 +752,32 @@ class ContentApi(object):
 | 
	
		
			
			| 740 | 752 |              resultset = resultset.filter(Content.parent_id==parent_id)
 | 
	
		
			
			| 741 | 753 |          if parent_id == 0 or parent_id is False:
 | 
	
		
			
			| 742 | 754 |              resultset = resultset.filter(Content.parent_id == None)
 | 
	
		
			
			| 743 |  | -        # parent_id == None give all contents
 | 
	
		
			
			| 744 |  | -
 | 
	
		
			
			| 745 |  | -        return resultset.all()
 | 
	
		
			
			| 746 |  | -
 | 
	
		
			
			| 747 |  | -    def get_children(self, parent_id: int, content_types: list, workspace: Workspace=None) -> typing.List[Content]:
 | 
	
		
			
			| 748 |  | -        """
 | 
	
		
			
			| 749 |  | -        Return parent_id childs of given content_types
 | 
	
		
			
			| 750 |  | -        :param parent_id: parent id
 | 
	
		
			
			| 751 |  | -        :param content_types: list of types
 | 
	
		
			
			| 752 |  | -        :param workspace: workspace filter
 | 
	
		
			
			| 753 |  | -        :return: list of content
 | 
	
		
			
			| 754 |  | -        """
 | 
	
		
			
			| 755 |  | -        resultset = self._base_query(workspace)
 | 
	
		
			
			| 756 |  | -        resultset = resultset.filter(Content.type.in_(content_types))
 | 
	
		
			
			| 757 | 755 |  
 | 
	
		
			
			| 758 |  | -        if parent_id:
 | 
	
		
			
			| 759 |  | -            resultset = resultset.filter(Content.parent_id==parent_id)
 | 
	
		
			
			| 760 |  | -        if parent_id is False:
 | 
	
		
			
			| 761 |  | -            resultset = resultset.filter(Content.parent_id == None)
 | 
	
		
			
			|  | 756 | +        return resultset
 | 
	
		
			
			| 762 | 757 |  
 | 
	
		
			
			| 763 |  | -        return resultset.all()
 | 
	
		
			
			|  | 758 | +    def get_all(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]:
 | 
	
		
			
			|  | 759 | +        return self._get_all_query(parent_id, content_type, workspace).all()
 | 
	
		
			
			| 764 | 760 |  
 | 
	
		
			
			|  | 761 | +    # TODO - G.M - 2018-07-17 - [Cleanup] Drop this method if unneeded
 | 
	
		
			
			|  | 762 | +    # def get_children(self, parent_id: int, content_types: list, workspace: Workspace=None) -> typing.List[Content]:
 | 
	
		
			
			|  | 763 | +    #     """
 | 
	
		
			
			|  | 764 | +    #     Return parent_id childs of given content_types
 | 
	
		
			
			|  | 765 | +    #     :param parent_id: parent id
 | 
	
		
			
			|  | 766 | +    #     :param content_types: list of types
 | 
	
		
			
			|  | 767 | +    #     :param workspace: workspace filter
 | 
	
		
			
			|  | 768 | +    #     :return: list of content
 | 
	
		
			
			|  | 769 | +    #     """
 | 
	
		
			
			|  | 770 | +    #     resultset = self._base_query(workspace)
 | 
	
		
			
			|  | 771 | +    #     resultset = resultset.filter(Content.type.in_(content_types))
 | 
	
		
			
			|  | 772 | +    #
 | 
	
		
			
			|  | 773 | +    #     if parent_id:
 | 
	
		
			
			|  | 774 | +    #         resultset = resultset.filter(Content.parent_id==parent_id)
 | 
	
		
			
			|  | 775 | +    #     if parent_id is False:
 | 
	
		
			
			|  | 776 | +    #         resultset = resultset.filter(Content.parent_id == None)
 | 
	
		
			
			|  | 777 | +    #
 | 
	
		
			
			|  | 778 | +    #     return resultset.all()
 | 
	
		
			
			|  | 779 | +
 | 
	
		
			
			|  | 780 | +    # TODO - G.M - 2018-07-17 - [Cleanup] Drop this method if unneeded
 | 
	
		
			
			| 765 | 781 |      # TODO find an other name to filter on is_deleted / is_archived
 | 
	
		
			
			| 766 | 782 |      def get_all_with_filter(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]:
 | 
	
		
			
			| 767 | 783 |          assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
 | 
	
	
		
			
			|  | @@ -781,33 +797,34 @@ class ContentApi(object):
 | 
	
		
			
			| 781 | 797 |  
 | 
	
		
			
			| 782 | 798 |          return resultset.all()
 | 
	
		
			
			| 783 | 799 |  
 | 
	
		
			
			| 784 |  | -    def get_all_without_exception(self, content_type: str, workspace: Workspace=None) -> typing.List[Content]:
 | 
	
		
			
			| 785 |  | -        assert content_type is not None# DYN_REMOVE
 | 
	
		
			
			| 786 |  | -
 | 
	
		
			
			| 787 |  | -        resultset = self._base_query(workspace)
 | 
	
		
			
			| 788 |  | -
 | 
	
		
			
			| 789 |  | -        if content_type != ContentType.Any:
 | 
	
		
			
			| 790 |  | -            resultset = resultset.filter(Content.type==content_type)
 | 
	
		
			
			| 791 |  | -
 | 
	
		
			
			| 792 |  | -        return resultset.all()
 | 
	
		
			
			| 793 |  | -
 | 
	
		
			
			| 794 |  | -    def get_last_active(self, parent_id: typing.Optional[int], content_type: str, workspace: Workspace=None, limit=10, offset=1) -> typing.List[Content]:
 | 
	
		
			
			| 795 |  | -        assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE
 | 
	
		
			
			| 796 |  | -        assert content_type is not None# DYN_REMOVE
 | 
	
		
			
			| 797 |  | -        assert isinstance(content_type, str) # DYN_REMOVE
 | 
	
		
			
			| 798 |  | -
 | 
	
		
			
			| 799 |  | -        resultset = self._base_query(workspace) \
 | 
	
		
			
			| 800 |  | -            .filter(Content.workspace_id == Workspace.workspace_id) \
 | 
	
		
			
			| 801 |  | -            .filter(Workspace.is_deleted.is_(False)) \
 | 
	
		
			
			| 802 |  | -            .order_by(desc(Content.updated))
 | 
	
		
			
			| 803 |  | -
 | 
	
		
			
			| 804 |  | -        if content_type!=ContentType.Any:
 | 
	
		
			
			| 805 |  | -            resultset = resultset.filter(Content.type==content_type)
 | 
	
		
			
			| 806 |  | -
 | 
	
		
			
			| 807 |  | -        if parent_id:
 | 
	
		
			
			| 808 |  | -            resultset = resultset.filter(Content.parent_id==parent_id)
 | 
	
		
			
			|  | 800 | +    # TODO - G.M - 2018-07-17 - [Cleanup] Drop this method if unneeded
 | 
	
		
			
			|  | 801 | +    # def get_all_without_exception(self, content_type: str, workspace: Workspace=None) -> typing.List[Content]:
 | 
	
		
			
			|  | 802 | +    #     assert content_type is not None# DYN_REMOVE
 | 
	
		
			
			|  | 803 | +    #
 | 
	
		
			
			|  | 804 | +    #     resultset = self._base_query(workspace)
 | 
	
		
			
			|  | 805 | +    #
 | 
	
		
			
			|  | 806 | +    #     if content_type != ContentType.Any:
 | 
	
		
			
			|  | 807 | +    #         resultset = resultset.filter(Content.type==content_type)
 | 
	
		
			
			|  | 808 | +    #
 | 
	
		
			
			|  | 809 | +    #     return resultset.all()
 | 
	
		
			
			|  | 810 | +
 | 
	
		
			
			|  | 811 | +    def get_last_active(
 | 
	
		
			
			|  | 812 | +            self,
 | 
	
		
			
			|  | 813 | +            parent_id: typing.Optional[int],
 | 
	
		
			
			|  | 814 | +            content_type: str,
 | 
	
		
			
			|  | 815 | +            workspace: Workspace=None,
 | 
	
		
			
			|  | 816 | +            limit: typing.Optional[int]=None,
 | 
	
		
			
			|  | 817 | +            offset: typing.Optional[int]= None,
 | 
	
		
			
			|  | 818 | +    ) -> typing.List[Content]:
 | 
	
		
			
			| 809 | 819 |  
 | 
	
		
			
			|  | 820 | +        resultset = self._get_all_query(
 | 
	
		
			
			|  | 821 | +            parent_id=parent_id,
 | 
	
		
			
			|  | 822 | +            content_type=content_type,
 | 
	
		
			
			|  | 823 | +            workspace=workspace,
 | 
	
		
			
			|  | 824 | +        )
 | 
	
		
			
			|  | 825 | +        resultset = resultset.order_by(desc(Content.updated))
 | 
	
		
			
			| 810 | 826 |          resultset = resultset.slice(start=offset, stop=limit)
 | 
	
		
			
			|  | 827 | +
 | 
	
		
			
			| 811 | 828 |          # result = []
 | 
	
		
			
			| 812 | 829 |          # for item in resultset:
 | 
	
		
			
			| 813 | 830 |          #     new_item = None
 |