| 
				
			 | 
			
			
				@@ -49,7 +49,7 @@ from tracim.models.data import Workspace 
			 | 
		
	
		
			
			| 
				49
			 | 
			
				49
			 | 
			
			
				 def compare_content_for_sorting_by_type_and_name( 
			 | 
		
	
		
			
			| 
				50
			 | 
			
				50
			 | 
			
			
				         content1: Content, 
			 | 
		
	
		
			
			| 
				51
			 | 
			
				51
			 | 
			
			
				         content2: Content 
			 | 
		
	
		
			
			| 
				52
			 | 
			
				
			 | 
			
			
				-): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				52
			 | 
			
			
				+) -> int: 
			 | 
		
	
		
			
			| 
				53
			 | 
			
				53
			 | 
			
			
				     """ 
			 | 
		
	
		
			
			| 
				54
			 | 
			
				54
			 | 
			
			
				     :param content1: 
			 | 
		
	
		
			
			| 
				55
			 | 
			
				55
			 | 
			
			
				     :param content2: 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -58,7 +58,7 @@ def compare_content_for_sorting_by_type_and_name( 
			 | 
		
	
		
			
			| 
				58
			 | 
			
				58
			 | 
			
			
				                 0 if content1 = content2 
			 | 
		
	
		
			
			| 
				59
			 | 
			
				59
			 | 
			
			
				     """ 
			 | 
		
	
		
			
			| 
				60
			 | 
			
				60
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				61
			 | 
			
				
			 | 
			
			
				-    if content1.type==content2.type: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				61
			 | 
			
			
				+    if content1.type == content2.type: 
			 | 
		
	
		
			
			| 
				62
			 | 
			
				62
			 | 
			
			
				         if content1.get_label().lower()>content2.get_label().lower(): 
			 | 
		
	
		
			
			| 
				63
			 | 
			
				63
			 | 
			
			
				             return 1 
			 | 
		
	
		
			
			| 
				64
			 | 
			
				64
			 | 
			
			
				         elif content1.get_label().lower()<content2.get_label().lower(): 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -66,11 +66,20 @@ def compare_content_for_sorting_by_type_and_name( 
			 | 
		
	
		
			
			| 
				66
			 | 
			
				66
			 | 
			
			
				         return 0 
			 | 
		
	
		
			
			| 
				67
			 | 
			
				67
			 | 
			
			
				     else: 
			 | 
		
	
		
			
			| 
				68
			 | 
			
				68
			 | 
			
			
				         # TODO - D.A. - 2014-12-02 - Manage Content Types Dynamically 
			 | 
		
	
		
			
			| 
				69
			 | 
			
				
			 | 
			
			
				-        content_type_order = [ContentType.Folder, ContentType.Page, ContentType.Thread, ContentType.File] 
			 | 
		
	
		
			
			| 
				70
			 | 
			
				
			 | 
			
			
				-        result = content_type_order.index(content1.type)-content_type_order.index(content2.type) 
			 | 
		
	
		
			
			| 
				71
			 | 
			
				
			 | 
			
			
				-        if result<0: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				69
			 | 
			
			
				+        content_type_order = [ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				70
			 | 
			
			
				+            ContentType.Folder, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				71
			 | 
			
			
				+            ContentType.Page, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				72
			 | 
			
			
				+            ContentType.Thread, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				73
			 | 
			
			
				+            ContentType.File, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				74
			 | 
			
			
				+        ] 
			 | 
		
	
		
			
			| 
				
			 | 
			
				75
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				76
			 | 
			
			
				+        content_1_type_index = content_type_order.index(content1.type) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				77
			 | 
			
			
				+        content_2_type_index = content_type_order.index(content2.type) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				78
			 | 
			
			
				+        result = content_1_type_index - content_2_type_index 
			 | 
		
	
		
			
			| 
				
			 | 
			
				79
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				80
			 | 
			
			
				+        if result < 0: 
			 | 
		
	
		
			
			| 
				72
			 | 
			
				81
			 | 
			
			
				             return -1 
			 | 
		
	
		
			
			| 
				73
			 | 
			
				
			 | 
			
			
				-        elif result>0: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				82
			 | 
			
			
				+        elif result > 0: 
			 | 
		
	
		
			
			| 
				74
			 | 
			
				83
			 | 
			
			
				             return 1 
			 | 
		
	
		
			
			| 
				75
			 | 
			
				84
			 | 
			
			
				         else: 
			 | 
		
	
		
			
			| 
				76
			 | 
			
				85
			 | 
			
			
				             return 0 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -79,7 +88,7 @@ def compare_content_for_sorting_by_type_and_name( 
			 | 
		
	
		
			
			| 
				79
			 | 
			
				88
			 | 
			
			
				 def compare_tree_items_for_sorting_by_type_and_name( 
			 | 
		
	
		
			
			| 
				80
			 | 
			
				89
			 | 
			
			
				         item1: NodeTreeItem, 
			 | 
		
	
		
			
			| 
				81
			 | 
			
				90
			 | 
			
			
				         item2: NodeTreeItem 
			 | 
		
	
		
			
			| 
				82
			 | 
			
				
			 | 
			
			
				-): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				91
			 | 
			
			
				+) -> int: 
			 | 
		
	
		
			
			| 
				83
			 | 
			
				92
			 | 
			
			
				     return compare_content_for_sorting_by_type_and_name(item1.node, item2.node) 
			 | 
		
	
		
			
			| 
				84
			 | 
			
				93
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				85
			 | 
			
				94
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -100,13 +109,13 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				100
			 | 
			
				109
			 | 
			
			
				             self, 
			 | 
		
	
		
			
			| 
				101
			 | 
			
				110
			 | 
			
			
				             session: Session, 
			 | 
		
	
		
			
			| 
				102
			 | 
			
				111
			 | 
			
			
				             current_user: typing.Optional[User], 
			 | 
		
	
		
			
			| 
				103
			 | 
			
				
			 | 
			
			
				-            show_archived=False, 
			 | 
		
	
		
			
			| 
				104
			 | 
			
				
			 | 
			
			
				-            show_deleted=False, 
			 | 
		
	
		
			
			| 
				105
			 | 
			
				
			 | 
			
			
				-            show_temporary=False, 
			 | 
		
	
		
			
			| 
				106
			 | 
			
				
			 | 
			
			
				-            all_content_in_treeview=True, 
			 | 
		
	
		
			
			| 
				107
			 | 
			
				
			 | 
			
			
				-            force_show_all_types=False, 
			 | 
		
	
		
			
			| 
				108
			 | 
			
				
			 | 
			
			
				-            disable_user_workspaces_filter=False, 
			 | 
		
	
		
			
			| 
				109
			 | 
			
				
			 | 
			
			
				-    ): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				112
			 | 
			
			
				+            show_archived: bool = False, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				113
			 | 
			
			
				+            show_deleted: bool = False, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				114
			 | 
			
			
				+            show_temporary: bool = False, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				115
			 | 
			
			
				+            all_content_in_treeview: bool = True, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				116
			 | 
			
			
				+            force_show_all_types: bool = False, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				117
			 | 
			
			
				+            disable_user_workspaces_filter: bool = False, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				118
			 | 
			
			
				+    ) -> None: 
			 | 
		
	
		
			
			| 
				110
			 | 
			
				119
			 | 
			
			
				         self._session = session 
			 | 
		
	
		
			
			| 
				111
			 | 
			
				120
			 | 
			
			
				         self._user = current_user 
			 | 
		
	
		
			
			| 
				112
			 | 
			
				121
			 | 
			
			
				         self._user_id = current_user.user_id if current_user else None 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -123,7 +132,7 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				123
			 | 
			
				132
			 | 
			
			
				             show_archived: bool=False, 
			 | 
		
	
		
			
			| 
				124
			 | 
			
				133
			 | 
			
			
				             show_deleted: bool=False, 
			 | 
		
	
		
			
			| 
				125
			 | 
			
				134
			 | 
			
			
				             show_temporary: bool=False, 
			 | 
		
	
		
			
			| 
				126
			 | 
			
				
			 | 
			
			
				-    ): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				135
			 | 
			
			
				+    ) -> typing.Generator['ContentApi', None, None]: 
			 | 
		
	
		
			
			| 
				127
			 | 
			
				136
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				128
			 | 
			
				137
			 | 
			
			
				         Use this method as context manager to update show_archived, 
			 | 
		
	
		
			
			| 
				129
			 | 
			
				138
			 | 
			
			
				         show_deleted and show_temporary properties during context. 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -145,11 +154,10 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				145
			 | 
			
				154
			 | 
			
			
				             self._show_deleted = previous_show_deleted 
			 | 
		
	
		
			
			| 
				146
			 | 
			
				155
			 | 
			
			
				             self._show_temporary = previous_show_temporary 
			 | 
		
	
		
			
			| 
				147
			 | 
			
				156
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				148
			 | 
			
				
			 | 
			
			
				-    def get_revision_join(self): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				157
			 | 
			
			
				+    def get_revision_join(self) -> sqlalchemy.sql.elements.BooleanClauseList: 
			 | 
		
	
		
			
			| 
				149
			 | 
			
				158
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				150
			 | 
			
				159
			 | 
			
			
				         Return the Content/ContentRevision query join condition 
			 | 
		
	
		
			
			| 
				151
			 | 
			
				160
			 | 
			
			
				         :return: Content/ContentRevision query join condition 
			 | 
		
	
		
			
			| 
				152
			 | 
			
				
			 | 
			
			
				-        :rtype sqlalchemy.sql.elements.BooleanClauseList 
			 | 
		
	
		
			
			| 
				153
			 | 
			
				161
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				154
			 | 
			
				162
			 | 
			
			
				         return and_(Content.id == ContentRevisionRO.content_id, 
			 | 
		
	
		
			
			| 
				155
			 | 
			
				163
			 | 
			
			
				                     ContentRevisionRO.revision_id == self._session.query( 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -159,32 +167,41 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				159
			 | 
			
				167
			 | 
			
			
				                     .limit(1) 
			 | 
		
	
		
			
			| 
				160
			 | 
			
				168
			 | 
			
			
				                     .correlate(Content)) 
			 | 
		
	
		
			
			| 
				161
			 | 
			
				169
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				162
			 | 
			
				
			 | 
			
			
				- 
			 | 
		
	
		
			
			| 
				163
			 | 
			
				
			 | 
			
			
				-    def get_canonical_query(self): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				170
			 | 
			
			
				+    def get_canonical_query(self) -> Query: 
			 | 
		
	
		
			
			| 
				164
			 | 
			
				171
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				165
			 | 
			
				172
			 | 
			
			
				         Return the Content/ContentRevision base query who join these table on the last revision. 
			 | 
		
	
		
			
			| 
				166
			 | 
			
				173
			 | 
			
			
				         :return: Content/ContentRevision Query 
			 | 
		
	
		
			
			| 
				167
			 | 
			
				
			 | 
			
			
				-        :rtype sqlalchemy.orm.query.Query 
			 | 
		
	
		
			
			| 
				168
			 | 
			
				174
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				169
			 | 
			
				
			 | 
			
			
				-        return self._session.query(Content).join(ContentRevisionRO, self.get_revision_join()) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				175
			 | 
			
			
				+        return self._session.query(Content)\ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				176
			 | 
			
			
				+            .join(ContentRevisionRO, self.get_revision_join()) 
			 | 
		
	
		
			
			| 
				170
			 | 
			
				177
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				171
			 | 
			
				178
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				172
			 | 
			
				
			 | 
			
			
				-    def sort_tree_items(cls, content_list: [NodeTreeItem])-> [Content]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				179
			 | 
			
			
				+    def sort_tree_items( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				180
			 | 
			
			
				+        cls, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				181
			 | 
			
			
				+        content_list: typing.List[NodeTreeItem], 
			 | 
		
	
		
			
			| 
				
			 | 
			
				182
			 | 
			
			
				+    )-> typing.List[NodeTreeItem]: 
			 | 
		
	
		
			
			| 
				173
			 | 
			
				183
			 | 
			
			
				         news = [] 
			 | 
		
	
		
			
			| 
				174
			 | 
			
				184
			 | 
			
			
				         for item in content_list: 
			 | 
		
	
		
			
			| 
				175
			 | 
			
				185
			 | 
			
			
				             news.append(item) 
			 | 
		
	
		
			
			| 
				176
			 | 
			
				186
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				177
			 | 
			
				
			 | 
			
			
				-        content_list.sort(key=cmp_to_key(compare_tree_items_for_sorting_by_type_and_name)) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				187
			 | 
			
			
				+        content_list.sort(key=cmp_to_key( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				188
			 | 
			
			
				+            compare_tree_items_for_sorting_by_type_and_name, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				189
			 | 
			
			
				+        )) 
			 | 
		
	
		
			
			| 
				178
			 | 
			
				190
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				179
			 | 
			
				191
			 | 
			
			
				         return content_list 
			 | 
		
	
		
			
			| 
				180
			 | 
			
				192
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				181
			 | 
			
				
			 | 
			
			
				- 
			 | 
		
	
		
			
			| 
				182
			 | 
			
				193
			 | 
			
			
				     @classmethod 
			 | 
		
	
		
			
			| 
				183
			 | 
			
				
			 | 
			
			
				-    def sort_content(cls, content_list: [Content])-> [Content]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				194
			 | 
			
			
				+    def sort_content( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				195
			 | 
			
			
				+        cls, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				196
			 | 
			
			
				+        content_list: typing.List[Content], 
			 | 
		
	
		
			
			| 
				
			 | 
			
				197
			 | 
			
			
				+    ) -> typing.List[Content]: 
			 | 
		
	
		
			
			| 
				184
			 | 
			
				198
			 | 
			
			
				         content_list.sort(key=cmp_to_key(compare_content_for_sorting_by_type_and_name)) 
			 | 
		
	
		
			
			| 
				185
			 | 
			
				199
			 | 
			
			
				         return content_list 
			 | 
		
	
		
			
			| 
				186
			 | 
			
				200
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				187
			 | 
			
				
			 | 
			
			
				-    def __real_base_query(self, workspace: Workspace=None): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				201
			 | 
			
			
				+    def __real_base_query( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				202
			 | 
			
			
				+        self, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				203
			 | 
			
			
				+        workspace: Workspace = None, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				204
			 | 
			
			
				+    ) -> Query: 
			 | 
		
	
		
			
			| 
				188
			 | 
			
				205
			 | 
			
			
				         result = self.get_canonical_query() 
			 | 
		
	
		
			
			| 
				189
			 | 
			
				206
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				190
			 | 
			
				207
			 | 
			
			
				         # Exclude non displayable types 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -212,7 +229,7 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				212
			 | 
			
				229
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				213
			 | 
			
				230
			 | 
			
			
				         return result 
			 | 
		
	
		
			
			| 
				214
			 | 
			
				231
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				215
			 | 
			
				
			 | 
			
			
				-    def _base_query(self, workspace: Workspace=None): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				232
			 | 
			
			
				+    def _base_query(self, workspace: Workspace=None) -> Query: 
			 | 
		
	
		
			
			| 
				216
			 | 
			
				233
			 | 
			
			
				         result = self.__real_base_query(workspace) 
			 | 
		
	
		
			
			| 
				217
			 | 
			
				234
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				218
			 | 
			
				235
			 | 
			
			
				         if not self._show_deleted: 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -226,7 +243,10 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				226
			 | 
			
				243
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				227
			 | 
			
				244
			 | 
			
			
				         return result 
			 | 
		
	
		
			
			| 
				228
			 | 
			
				245
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				229
			 | 
			
				
			 | 
			
			
				-    def __revisions_real_base_query(self, workspace: Workspace=None): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				246
			 | 
			
			
				+    def __revisions_real_base_query( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				247
			 | 
			
			
				+        self, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				248
			 | 
			
			
				+        workspace: Workspace=None, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				249
			 | 
			
			
				+    ) -> Query: 
			 | 
		
	
		
			
			| 
				230
			 | 
			
				250
			 | 
			
			
				         result = self._session.query(ContentRevisionRO) 
			 | 
		
	
		
			
			| 
				231
			 | 
			
				251
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				232
			 | 
			
				252
			 | 
			
			
				         # Exclude non displayable types 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -245,7 +265,10 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				245
			 | 
			
				265
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				246
			 | 
			
				266
			 | 
			
			
				         return result 
			 | 
		
	
		
			
			| 
				247
			 | 
			
				267
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				248
			 | 
			
				
			 | 
			
			
				-    def _revisions_base_query(self, workspace: Workspace=None): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				268
			 | 
			
			
				+    def _revisions_base_query( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				269
			 | 
			
			
				+        self, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				270
			 | 
			
			
				+        workspace: Workspace=None, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				271
			 | 
			
			
				+    ) -> Query: 
			 | 
		
	
		
			
			| 
				249
			 | 
			
				272
			 | 
			
			
				         result = self.__revisions_real_base_query(workspace) 
			 | 
		
	
		
			
			| 
				250
			 | 
			
				273
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				251
			 | 
			
				274
			 | 
			
			
				         if not self._show_deleted: 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -259,7 +282,10 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				259
			 | 
			
				282
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				260
			 | 
			
				283
			 | 
			
			
				         return result 
			 | 
		
	
		
			
			| 
				261
			 | 
			
				284
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				262
			 | 
			
				
			 | 
			
			
				-    def _hard_filtered_base_query(self, workspace: Workspace=None): 
			 | 
		
	
		
			
			| 
				
			 | 
			
				285
			 | 
			
			
				+    def _hard_filtered_base_query( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				286
			 | 
			
			
				+        self, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				287
			 | 
			
			
				+        workspace: Workspace=None, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				288
			 | 
			
			
				+    ) -> Query: 
			 | 
		
	
		
			
			| 
				263
			 | 
			
				289
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				264
			 | 
			
				290
			 | 
			
			
				         If set to True, then filterign on is_deleted and is_archived will also 
			 | 
		
	
		
			
			| 
				265
			 | 
			
				291
			 | 
			
			
				         filter parent properties. This is required for search() function which 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -291,10 +317,13 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				291
			 | 
			
				317
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				292
			 | 
			
				318
			 | 
			
			
				         return result 
			 | 
		
	
		
			
			| 
				293
			 | 
			
				319
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				294
			 | 
			
				
			 | 
			
			
				-    def get_base_query(self, workspace: Workspace) -> Query: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				320
			 | 
			
			
				+    def get_base_query( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				321
			 | 
			
			
				+        self, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				322
			 | 
			
			
				+        workspace: Workspace, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				323
			 | 
			
			
				+    ) -> Query: 
			 | 
		
	
		
			
			| 
				295
			 | 
			
				324
			 | 
			
			
				         return self._base_query(workspace) 
			 | 
		
	
		
			
			| 
				296
			 | 
			
				325
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				297
			 | 
			
				
			 | 
			
			
				-    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) -> [Content]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				326
			 | 
			
			
				+    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]: 
			 | 
		
	
		
			
			| 
				298
			 | 
			
				327
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				299
			 | 
			
				328
			 | 
			
			
				         This method returns child items (folders or items) for left bar treeview. 
			 | 
		
	
		
			
			| 
				300
			 | 
			
				329
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -306,6 +335,9 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				306
			 | 
			
				335
			 | 
			
			
				                For example, if you want to move a Page from a folder to another, you should show only folders that accept pages 
			 | 
		
	
		
			
			| 
				307
			 | 
			
				336
			 | 
			
			
				         :return: 
			 | 
		
	
		
			
			| 
				308
			 | 
			
				337
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				338
			 | 
			
			
				+        filter_by_allowed_content_types = filter_by_allowed_content_types or []  # FDV 
			 | 
		
	
		
			
			| 
				
			 | 
			
				339
			 | 
			
			
				+        removed_item_ids = removed_item_ids or []  # FDV 
			 | 
		
	
		
			
			| 
				
			 | 
			
				340
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				309
			 | 
			
				341
			 | 
			
			
				         if not allowed_node_types: 
			 | 
		
	
		
			
			| 
				310
			 | 
			
				342
			 | 
			
			
				             allowed_node_types = [ContentType.Folder] 
			 | 
		
	
		
			
			| 
				311
			 | 
			
				343
			 | 
			
			
				         elif allowed_node_types==ContentType.Any: 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -332,7 +364,11 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				332
			 | 
			
				364
			 | 
			
			
				         result = [] 
			 | 
		
	
		
			
			| 
				333
			 | 
			
				365
			 | 
			
			
				         for folder in folders: 
			 | 
		
	
		
			
			| 
				334
			 | 
			
				366
			 | 
			
			
				             for allowed_content_type in filter_by_allowed_content_types: 
			 | 
		
	
		
			
			| 
				335
			 | 
			
				
			 | 
			
			
				-                if folder.type==ContentType.Folder and folder.properties['allowed_content'][allowed_content_type]==True: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				367
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				368
			 | 
			
			
				+                is_folder = folder.type == ContentType.Folder 
			 | 
		
	
		
			
			| 
				
			 | 
			
				369
			 | 
			
			
				+                content_type__allowed = folder.properties['allowed_content'][allowed_content_type] == True 
			 | 
		
	
		
			
			| 
				
			 | 
			
				370
			 | 
			
			
				+ 
			 | 
		
	
		
			
			| 
				
			 | 
			
				371
			 | 
			
			
				+                if is_folder and content_type__allowed: 
			 | 
		
	
		
			
			| 
				336
			 | 
			
				372
			 | 
			
			
				                     result.append(folder) 
			 | 
		
	
		
			
			| 
				337
			 | 
			
				373
			 | 
			
			
				                     break 
			 | 
		
	
		
			
			| 
				338
			 | 
			
				374
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -636,7 +672,7 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				636
			 | 
			
				672
			 | 
			
			
				             ), 
			 | 
		
	
		
			
			| 
				637
			 | 
			
				673
			 | 
			
			
				         )) 
			 | 
		
	
		
			
			| 
				638
			 | 
			
				674
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				639
			 | 
			
				
			 | 
			
			
				-    def get_all(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> [Content]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				675
			 | 
			
			
				+    def get_all(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]: 
			 | 
		
	
		
			
			| 
				640
			 | 
			
				676
			 | 
			
			
				         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE 
			 | 
		
	
		
			
			| 
				641
			 | 
			
				677
			 | 
			
			
				         assert content_type is not None# DYN_REMOVE 
			 | 
		
	
		
			
			| 
				642
			 | 
			
				678
			 | 
			
			
				         assert isinstance(content_type, str) # DYN_REMOVE 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -653,7 +689,7 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				653
			 | 
			
				689
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				654
			 | 
			
				690
			 | 
			
			
				         return resultset.all() 
			 | 
		
	
		
			
			| 
				655
			 | 
			
				691
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				656
			 | 
			
				
			 | 
			
			
				-    def get_children(self, parent_id: int, content_types: list, workspace: Workspace=None) -> [Content]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				692
			 | 
			
			
				+    def get_children(self, parent_id: int, content_types: list, workspace: Workspace=None) -> typing.List[Content]: 
			 | 
		
	
		
			
			| 
				657
			 | 
			
				693
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				658
			 | 
			
				694
			 | 
			
			
				         Return parent_id childs of given content_types 
			 | 
		
	
		
			
			| 
				659
			 | 
			
				695
			 | 
			
			
				         :param parent_id: parent id 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -672,7 +708,7 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				672
			 | 
			
				708
			 | 
			
			
				         return resultset.all() 
			 | 
		
	
		
			
			| 
				673
			 | 
			
				709
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				674
			 | 
			
				710
			 | 
			
			
				     # TODO find an other name to filter on is_deleted / is_archived 
			 | 
		
	
		
			
			| 
				675
			 | 
			
				
			 | 
			
			
				-    def get_all_with_filter(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> [Content]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				711
			 | 
			
			
				+    def get_all_with_filter(self, parent_id: int=None, content_type: str=ContentType.Any, workspace: Workspace=None) -> typing.List[Content]: 
			 | 
		
	
		
			
			| 
				676
			 | 
			
				712
			 | 
			
			
				         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE 
			 | 
		
	
		
			
			| 
				677
			 | 
			
				713
			 | 
			
			
				         assert content_type is not None# DYN_REMOVE 
			 | 
		
	
		
			
			| 
				678
			 | 
			
				714
			 | 
			
			
				         assert isinstance(content_type, str) # DYN_REMOVE 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -690,7 +726,7 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				690
			 | 
			
				726
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				691
			 | 
			
				727
			 | 
			
			
				         return resultset.all() 
			 | 
		
	
		
			
			| 
				692
			 | 
			
				728
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				693
			 | 
			
				
			 | 
			
			
				-    def get_all_without_exception(self, content_type: str, workspace: Workspace=None) -> [Content]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				729
			 | 
			
			
				+    def get_all_without_exception(self, content_type: str, workspace: Workspace=None) -> typing.List[Content]: 
			 | 
		
	
		
			
			| 
				694
			 | 
			
				730
			 | 
			
			
				         assert content_type is not None# DYN_REMOVE 
			 | 
		
	
		
			
			| 
				695
			 | 
			
				731
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				696
			 | 
			
				732
			 | 
			
			
				         resultset = self._base_query(workspace) 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -700,7 +736,7 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				700
			 | 
			
				736
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				701
			 | 
			
				737
			 | 
			
			
				         return resultset.all() 
			 | 
		
	
		
			
			| 
				702
			 | 
			
				738
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				703
			 | 
			
				
			 | 
			
			
				-    def get_last_active(self, parent_id: int, content_type: str, workspace: Workspace=None, limit=10) -> [Content]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				739
			 | 
			
			
				+    def get_last_active(self, parent_id: int, content_type: str, workspace: Workspace=None, limit=10) -> typing.List[Content]: 
			 | 
		
	
		
			
			| 
				704
			 | 
			
				740
			 | 
			
			
				         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE 
			 | 
		
	
		
			
			| 
				705
			 | 
			
				741
			 | 
			
			
				         assert content_type is not None# DYN_REMOVE 
			 | 
		
	
		
			
			| 
				706
			 | 
			
				742
			 | 
			
			
				         assert isinstance(content_type, str) # DYN_REMOVE 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -736,7 +772,7 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				736
			 | 
			
				772
			 | 
			
			
				         return result 
			 | 
		
	
		
			
			| 
				737
			 | 
			
				773
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				738
			 | 
			
				774
			 | 
			
			
				     def get_last_unread(self, parent_id: int, content_type: str, 
			 | 
		
	
		
			
			| 
				739
			 | 
			
				
			 | 
			
			
				-                        workspace: Workspace=None, limit=10) -> [Content]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				775
			 | 
			
			
				+                        workspace: Workspace=None, limit=10) -> typing.List[Content]: 
			 | 
		
	
		
			
			| 
				740
			 | 
			
				776
			 | 
			
			
				         assert parent_id is None or isinstance(parent_id, int) # DYN_REMOVE 
			 | 
		
	
		
			
			| 
				741
			 | 
			
				777
			 | 
			
			
				         assert content_type is not None# DYN_REMOVE 
			 | 
		
	
		
			
			| 
				742
			 | 
			
				778
			 | 
			
			
				         assert isinstance(content_type, str) # DYN_REMOVE 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -1106,7 +1142,7 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				1106
			 | 
			
				1142
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				1107
			 | 
			
				1143
			 | 
			
			
				         return keywords 
			 | 
		
	
		
			
			| 
				1108
			 | 
			
				1144
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				1109
			 | 
			
				
			 | 
			
			
				-    def search(self, keywords: [str]) -> sqlalchemy.orm.query.Query: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				1145
			 | 
			
			
				+    def search(self, keywords: [str]) -> Query: 
			 | 
		
	
		
			
			| 
				1110
			 | 
			
				1146
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				1111
			 | 
			
				1147
			 | 
			
			
				         :return: a sorted list of Content items 
			 | 
		
	
		
			
			| 
				1112
			 | 
			
				1148
			 | 
			
			
				         """ 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -1123,7 +1159,7 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				1123
			 | 
			
				1159
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				1124
			 | 
			
				1160
			 | 
			
			
				         return title_keyworded_items 
			 | 
		
	
		
			
			| 
				1125
			 | 
			
				1161
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				1126
			 | 
			
				
			 | 
			
			
				-    def get_all_types(self) -> [ContentType]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				1162
			 | 
			
			
				+    def get_all_types(self) -> typing.List[ContentType]: 
			 | 
		
	
		
			
			| 
				1127
			 | 
			
				1163
			 | 
			
			
				         labels = ContentType.all() 
			 | 
		
	
		
			
			| 
				1128
			 | 
			
				1164
			 | 
			
			
				         content_types = [] 
			 | 
		
	
		
			
			| 
				1129
			 | 
			
				1165
			 | 
			
			
				         for label in labels: 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -1131,7 +1167,10 @@ class ContentApi(object): 
			 | 
		
	
		
			
			| 
				1131
			 | 
			
				1167
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				1132
			 | 
			
				1168
			 | 
			
			
				         return ContentType.sorted(content_types) 
			 | 
		
	
		
			
			| 
				1133
			 | 
			
				1169
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				1134
			 | 
			
				
			 | 
			
			
				-    def exclude_unavailable(self, contents: [Content]) -> [Content]: 
			 | 
		
	
		
			
			| 
				
			 | 
			
				1170
			 | 
			
			
				+    def exclude_unavailable( 
			 | 
		
	
		
			
			| 
				
			 | 
			
				1171
			 | 
			
			
				+        self, 
			 | 
		
	
		
			
			| 
				
			 | 
			
				1172
			 | 
			
			
				+        contents: typing.List[Content], 
			 | 
		
	
		
			
			| 
				
			 | 
			
				1173
			 | 
			
			
				+    ) -> typing.List[Content]: 
			 | 
		
	
		
			
			| 
				1135
			 | 
			
				1174
			 | 
			
			
				         """ 
			 | 
		
	
		
			
			| 
				1136
			 | 
			
				1175
			 | 
			
			
				         Update and return list with content under archived/deleted removed. 
			 | 
		
	
		
			
			| 
				1137
			 | 
			
				1176
			 | 
			
			
				         :param contents: List of contents to parse 
			 |