Browse Source

Issue #85: Restore Archive buttons and permet display of archives and deleted contents

Bastien Sevajol (Algoo) 8 years ago
parent
commit
7d4d07b7a0

+ 10 - 2
tracim/tracim/controllers/__init__.py View File

@@ -73,7 +73,11 @@ class TIMRestPathContextSetup(object):
73 73
 
74 74
     @classmethod
75 75
     def current_folder(cls) -> Content:
76
-        content_api = ContentApi(tg.tmpl_context.current_user)
76
+        content_api = ContentApi(
77
+            tg.tmpl_context.current_user,
78
+            show_archived=True,
79
+            show_deleted=True,
80
+        )
77 81
         folder_id = int(tg.request.controller_state.routing_args.get('folder_id'))
78 82
         folder = content_api.get_one(folder_id, ContentType.Folder, tg.tmpl_context.workspace)
79 83
 
@@ -149,7 +153,11 @@ class TIMRestControllerWithBreadcrumb(TIMRestController):
149 153
         :param item_id: an item id (item may be normal content or folder
150 154
         :return:
151 155
         """
152
-        return ContentApi(tmpl_context.current_user).build_breadcrumb(tmpl_context.workspace, item_id)
156
+        return ContentApi(
157
+            tmpl_context.current_user,
158
+            show_archived=True,
159
+            show_deleted=True,
160
+        ).build_breadcrumb(tmpl_context.workspace, item_id)
153 161
 
154 162
     def _struct_new_serialized(self, workspace_id, parent_id):
155 163
         print('values are: ', workspace_id, parent_id)

+ 46 - 11
tracim/tracim/controllers/content.py View File

@@ -174,13 +174,16 @@ class UserWorkspaceFolderFileRestController(TIMWorkspaceContentRestController):
174 174
         file_id = int(file_id)
175 175
         user = tmpl_context.current_user
176 176
         workspace = tmpl_context.workspace
177
-        workspace_id = tmpl_context.workspace_id
178 177
 
179 178
         current_user_content = Context(CTX.CURRENT_USER,
180 179
                                        current_user=user).toDict(user)
181 180
         current_user_content.roles.sort(key=lambda role: role.workspace.name)
182 181
 
183
-        content_api = ContentApi(user)
182
+        content_api = ContentApi(
183
+            user,
184
+            show_archived=True,
185
+            show_deleted=True,
186
+        )
184 187
         if revision_id:
185 188
             file = content_api.get_one_from_revision(file_id,  self._item_type, workspace, revision_id)
186 189
         else:
@@ -402,7 +405,11 @@ class UserWorkspaceFolderPageRestController(TIMWorkspaceContentRestController):
402 405
         current_user_content = Context(CTX.CURRENT_USER).toDict(user)
403 406
         current_user_content.roles.sort(key=lambda role: role.workspace.name)
404 407
 
405
-        content_api = ContentApi(user)
408
+        content_api = ContentApi(
409
+            user,
410
+            show_deleted=True,
411
+            show_archived=True,
412
+        )
406 413
         if revision_id:
407 414
             page = content_api.get_one_from_revision(page_id, ContentType.Page, workspace, revision_id)
408 415
         else:
@@ -587,7 +594,11 @@ class UserWorkspaceFolderThreadRestController(TIMWorkspaceContentRestController)
587 594
         current_user_content = Context(CTX.CURRENT_USER).toDict(user)
588 595
         current_user_content.roles.sort(key=lambda role: role.workspace.name)
589 596
 
590
-        content_api = ContentApi(user)
597
+        content_api = ContentApi(
598
+            user,
599
+            show_deleted=True,
600
+            show_archived=True,
601
+        )
591 602
         thread = content_api.get_one(thread_id, ContentType.Thread, workspace)
592 603
 
593 604
         fake_api_breadcrumb = self.get_breadcrumb(thread_id)
@@ -744,18 +755,35 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
744 755
 
745 756
     @tg.require(current_user_is_reader())
746 757
     @tg.expose('tracim.templates.folder.getone')
747
-    def get_one(self, folder_id):
758
+    def get_one(self, folder_id, **kwargs):
759
+        """
760
+        :param folder_id: Displayed folder id
761
+        :param kwargs:
762
+          * show_deleted: bool: Display deleted contents or hide them if False
763
+          * show_archived: bool: Display archived contents or hide them
764
+            if False
765
+        """
766
+        show_deleted = kwargs.get('show_deleted', False)
767
+        show_archived = kwargs.get('show_archived', False)
748 768
         folder_id = int(folder_id)
749 769
         user = tmpl_context.current_user
750 770
         workspace = tmpl_context.workspace
751
-        workspace_id = tmpl_context.workspace_id
752 771
 
753 772
         current_user_content = Context(CTX.CURRENT_USER,
754 773
                                        current_user=user).toDict(user)
755 774
         current_user_content.roles.sort(key=lambda role: role.workspace.name)
756 775
 
757
-        content_api = ContentApi(user)
758
-        folder = content_api.get_one(folder_id, ContentType.Folder, workspace)
776
+        content_api = ContentApi(
777
+            user,
778
+            show_deleted=show_deleted,
779
+            show_archived=show_archived,
780
+        )
781
+        with content_api.show(show_deleted=True, show_archived=True):
782
+            folder = content_api.get_one(
783
+                folder_id,
784
+                ContentType.Folder,
785
+                workspace,
786
+            )
759 787
 
760 788
         fake_api_breadcrumb = self.get_breadcrumb(folder_id)
761 789
         fake_api_subfolders = self.get_all_fake(workspace, folder.content_id).result
@@ -787,10 +815,16 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
787 815
         fake_api.sub_items = Context(CTX.FOLDER_CONTENT_LIST).toDict(sub_items)
788 816
 
789 817
         fake_api.content_types = Context(CTX.DEFAULT).toDict(
790
-            content_api.get_all_types())
818
+            content_api.get_all_types()
819
+        )
791 820
 
792 821
         dictified_folder = Context(CTX.FOLDER).toDict(folder, 'folder')
793
-        return DictLikeClass(result = dictified_folder, fake_api=fake_api)
822
+        return DictLikeClass(
823
+            result=dictified_folder,
824
+            fake_api=fake_api,
825
+            show_deleted=show_deleted,
826
+            show_archived=show_archived,
827
+        )
794 828
 
795 829
 
796 830
     def get_all_fake(self, context_workspace: Workspace, parent_id=None):
@@ -804,7 +838,8 @@ class UserWorkspaceFolderRestController(TIMRestControllerWithBreadcrumb):
804 838
         """
805 839
         workspace = context_workspace
806 840
         content_api = ContentApi(tmpl_context.current_user)
807
-        parent_folder = content_api.get_one(parent_id, ContentType.Folder)
841
+        with content_api.show(show_deleted=True, show_archived=True):
842
+            parent_folder = content_api.get_one(parent_id, ContentType.Folder)
808 843
         folders = content_api.get_child_folders(parent_folder, workspace)
809 844
 
810 845
         folders = Context(CTX.FOLDERS).toDict(folders)

+ 17 - 2
tracim/tracim/controllers/workspace.py View File

@@ -42,7 +42,16 @@ class UserWorkspaceRestController(TIMRestController):
42 42
         tg.redirect(tg.url('/home'))
43 43
 
44 44
     @tg.expose('tracim.templates.workspace.getone')
45
-    def get_one(self, workspace_id):
45
+    def get_one(self, workspace_id, **kwargs):
46
+        """
47
+        :param workspace_id: Displayed workspace id
48
+        :param kwargs:
49
+          * show_deleted: bool: Display deleted contents or hide them if False
50
+          * show_archived: bool: Display archived contents or hide them
51
+            if False
52
+        """
53
+        show_deleted = kwargs.get('show_deleted', False)
54
+        show_archived = kwargs.get('show_archived', False)
46 55
         user = tmpl_context.current_user
47 56
 
48 57
         current_user_content = Context(CTX.CURRENT_USER).toDict(user)
@@ -61,7 +70,11 @@ class UserWorkspaceRestController(TIMRestController):
61 70
 
62 71
         fake_api.sub_items = Context(CTX.FOLDER_CONTENT_LIST).toDict(
63 72
             # TODO BS 20161209: Is the correct way to grab folders? No use API?
64
-            workspace.get_valid_children(ContentApi.DISPLAYABLE_CONTENTS)
73
+            workspace.get_valid_children(
74
+                ContentApi.DISPLAYABLE_CONTENTS,
75
+                show_deleted=show_deleted,
76
+                show_archived=show_archived,
77
+            )
65 78
         )
66 79
 
67 80
         dictified_workspace = Context(CTX.WORKSPACE).toDict(workspace, 'workspace')
@@ -71,6 +84,8 @@ class UserWorkspaceRestController(TIMRestController):
71 84
             result=dictified_workspace,
72 85
             fake_api=fake_api,
73 86
             webdav_url=webdav_url,
87
+            show_deleted=show_deleted,
88
+            show_archived=show_archived,
74 89
         )
75 90
 
76 91
     @tg.expose('json')

+ 30 - 0
tracim/tracim/lib/content.py View File

@@ -1,4 +1,6 @@
1 1
 # -*- coding: utf-8 -*-
2
+from contextlib import contextmanager
3
+
2 4
 import os
3 5
 
4 6
 from operator import itemgetter
@@ -100,6 +102,34 @@ class ContentApi(object):
100 102
         self._force_show_all_types = force_show_all_types
101 103
         self._disable_user_workspaces_filter = disable_user_workspaces_filter
102 104
 
105
+    @contextmanager
106
+    def show(
107
+            self,
108
+            show_archived: bool=False,
109
+            show_deleted: bool=False,
110
+            show_temporary: bool=False,
111
+    ):
112
+        """
113
+        Use this method as context manager to update show_archived,
114
+        show_deleted and show_temporary properties during context.
115
+        :param show_archived: show archived contents
116
+        :param show_deleted:  show deleted contents
117
+        :param show_temporary:  show temporary contents
118
+        """
119
+        previous_show_archived = self._show_archived
120
+        previous_show_deleted = self._show_deleted
121
+        previous_show_temporary = self._show_temporary
122
+
123
+        try:
124
+            self._show_archived = show_archived
125
+            self._show_deleted = show_deleted
126
+            self._show_temporary = show_temporary
127
+            yield self
128
+        finally:
129
+            self._show_archived = previous_show_archived
130
+            self._show_deleted = previous_show_deleted
131
+            self._show_temporary = previous_show_temporary
132
+
103 133
     @classmethod
104 134
     def get_revision_join(cls):
105 135
         """

+ 5 - 1
tracim/tracim/lib/helpers.py View File

@@ -167,7 +167,11 @@ def convert_id_into_instances(id: str) -> (Workspace, Content):
167 167
     try:
168 168
         content_data = content_str.split(CST.TREEVIEW_MENU.ID_SEPARATOR)
169 169
         content_id = int(content_data[1])
170
-        content = ContentApi(tg.tmpl_context.current_user).get_one(content_id, ContentType.Any)
170
+        content = ContentApi(
171
+            tg.tmpl_context.current_user,
172
+            show_archived=True,
173
+            show_deleted=True,
174
+        ).get_one(content_id, ContentType.Any)
171 175
     except (IndexError, ValueError) as e:
172 176
         content = None
173 177
 

+ 12 - 3
tracim/tracim/model/data.py View File

@@ -97,12 +97,17 @@ class Workspace(DeclarativeBase):
97 97
         # @see Content.get_allowed_content_types()
98 98
         return [ContentType('folder')]
99 99
 
100
-    def get_valid_children(self, content_types: list=None):
100
+    def get_valid_children(
101
+            self,
102
+            content_types: list=None,
103
+            show_deleted: bool=False,
104
+            show_archived: bool=False,
105
+    ):
101 106
         for child in self.contents:
102 107
             # we search only direct children
103 108
             if not child.parent \
104
-                    and not child.is_deleted \
105
-                    and not child.is_archived:
109
+                    and (show_deleted or not child.is_deleted) \
110
+                    and (show_archived or not child.is_archived):
106 111
                 if not content_types or child.type in content_types:
107 112
                     yield child
108 113
 
@@ -1032,6 +1037,10 @@ class Content(DeclarativeBase):
1032 1037
     def revision(self) -> ContentRevisionRO:
1033 1038
         return self.get_current_revision()
1034 1039
 
1040
+    @property
1041
+    def is_editable(self) -> bool:
1042
+        return not self.is_archived and not self.is_deleted
1043
+
1035 1044
     def get_current_revision(self) -> ContentRevisionRO:
1036 1045
         if not self.revisions:
1037 1046
             return self.new_revision()

+ 20 - 3
tracim/tracim/model/serializers.py View File

@@ -265,7 +265,7 @@ def serialize_version_for_page_or_file(version: ContentRevisionRO, context: Cont
265 265
         label = version.label,
266 266
         owner = context.toDict(version.owner),
267 267
         created = version.created,
268
-        action = context.toDict(version.get_last_action())
268
+        action = context.toDict(version.get_last_action()),
269 269
     )
270 270
 
271 271
 
@@ -389,6 +389,9 @@ def serialize_node_for_page(content: Content, context: Context):
389 389
             revisions=context.toDict(sorted(content.revisions, key=lambda v: v.created, reverse=True)),
390 390
             selected_revision='latest' if content.revision_to_serialize<=0 else content.revision_to_serialize,
391 391
             history=Context(CTX.CONTENT_HISTORY).toDict(content.get_history()),
392
+            is_editable=content.is_editable,
393
+            is_deleted=content.is_deleted,
394
+            is_archived=content.is_archived,
392 395
             urls = context.toDict({
393 396
                 'mark_read': context.url(Content.format_path('/workspaces/{wid}/folders/{fid}/{ctype}s/{cid}/put_read', content)),
394 397
                 'mark_unread': context.url(Content.format_path('/workspaces/{wid}/folders/{fid}/{ctype}s/{cid}/put_unread', content))
@@ -458,6 +461,9 @@ def serialize_node_for_page(item: Content, context: Context):
458 461
             comments = reversed(context.toDict(item.get_comments())),
459 462
             is_new=item.has_new_information_for(context.get_user()),
460 463
             history = Context(CTX.CONTENT_HISTORY).toDict(item.get_history()),
464
+            is_editable=item.is_editable,
465
+            is_deleted=item.is_deleted,
466
+            is_archived=item.is_archived,
461 467
             urls = context.toDict({
462 468
                 'mark_read': context.url(Content.format_path('/workspaces/{wid}/folders/{fid}/{ctype}s/{cid}/put_read', item)),
463 469
                 'mark_unread': context.url(Content.format_path('/workspaces/{wid}/folders/{fid}/{ctype}s/{cid}/put_unread', item))
@@ -547,7 +553,8 @@ def serialize_content_for_workspace(content: Content, context: Context):
547 553
                 all = page_nb_all,
548 554
                 open = page_nb_open,
549 555
             ),
550
-            content_nb = DictLikeClass(all = content_nb_all)
556
+            content_nb = DictLikeClass(all = content_nb_all),
557
+            is_editable=content.is_editable,
551 558
         )
552 559
 
553 560
     return result
@@ -595,7 +602,10 @@ def serialize_content_for_workspace_and_folder(content: Content, context: Contex
595 602
                                     open=folder_nb_open),
596 603
             page_nb=DictLikeClass(all=page_nb_all,
597 604
                                   open=page_nb_open),
598
-            content_nb=DictLikeClass(all = content_nb_all)
605
+            content_nb=DictLikeClass(all = content_nb_all),
606
+            is_archived=content.is_archived,
607
+            is_deleted=content.is_deleted,
608
+            is_editable=content.is_editable,
599 609
         )
600 610
 
601 611
     elif content.type==ContentType.Page:
@@ -635,6 +645,9 @@ def serialize_content_for_general_list(content: Content, context: Context):
635 645
         url=ContentType.fill_url(content),
636 646
         type=DictLikeClass(content_type.toDict()),
637 647
         status=context.toDict(content.get_status()),
648
+        is_deleted=content.is_deleted,
649
+        is_archived=content.is_archived,
650
+        is_editable=content.is_editable,
638 651
         last_activity = DictLikeClass({'date': last_activity_date,
639 652
                                        'label': last_activity_date_formatted,
640 653
                                        'delta': last_activity_label})
@@ -706,6 +719,10 @@ def serialize_content_for_folder_content_list(content: Content, context: Context
706 719
         item = Context(CTX.CONTENT_LIST).toDict(content)
707 720
         item.notes = ''
708 721
 
722
+    item.is_deleted = content.is_deleted
723
+    item.is_archived = content.is_archived
724
+    item.is_editable = content.is_editable
725
+
709 726
     return item
710 727
 
711 728
 

+ 4 - 0
tracim/tracim/templates/file/getone.mak View File

@@ -36,6 +36,9 @@
36 36
 ##
37 37
 ############################################################################
38 38
 
39
+<div class="content-container ${'not-editable' if not result.file.is_editable else ''}">
40
+<!--# TODO BS 20161213: Indent content-->
41
+
39 42
 <div class="row t-page-header-row">
40 43
     <div class="col-sm-7 col-sm-offset-3 main">
41 44
         <h1 class="page-header t-file-color-border">
@@ -167,3 +170,4 @@
167 170
     </div>
168 171
 <div/>
169 172
 
173
+</div>

+ 33 - 8
tracim/tracim/templates/file/toolbar.mak View File

@@ -3,16 +3,19 @@
3 3
 <%namespace name="BUTTON" file="tracim.templates.widgets.button"/>
4 4
 
5 5
 <%def name="SECURED_FILE(user, workspace, file)">
6
-    <div class="btn-group btn-group-vertical text-center">
7
-        ${BUTTON.MARK_CONTENT_READ_OR_UNREAD(user, workspace, file)}
8
-    </div>
9
-    <hr class="t-toolbar-btn-group-separator"/>
10
-    <p></p>
6
+
7
+    % if file.is_editable:
8
+        <div class="btn-group btn-group-vertical text-center">
9
+            ${BUTTON.MARK_CONTENT_READ_OR_UNREAD(user, workspace, file)}
10
+        </div>
11
+        <hr class="t-toolbar-btn-group-separator"/>
12
+        <p></p>
13
+    % endif
11 14
 
12 15
     <% download_url = tg.url('/workspaces/{}/folders/{}/files/{}/download?revision_id={}'.format(result.file.workspace.id, result.file.parent.id,result.file.id,result.file.selected_revision)) %>
13 16
     <% edit_disabled = ('', 'disabled')[file.selected_revision!='latest' or file.status.id[:6]=='closed'] %>
14 17
     <% delete_or_archive_disabled = ('', 'disabled')[file.selected_revision!='latest'] %>
15
-    % if h.user_role(user, workspace)>1:
18
+    % if h.user_role(user, workspace)>1 and file.is_editable:
16 19
         <div class="btn-group btn-group-vertical">
17 20
             <a title="${_('Edit current file')}" class="btn btn-default ${edit_disabled}" data-toggle="modal" data-target="#file-edit-modal-dialog" data-remote="${tg.url('/workspaces/{}/folders/{}/files/{}/edit'.format(file.workspace.id, file.parent.id, file.id))}" >${ICON.FA_FW('fa fa-edit t-less-visible')} ${_('Edit')}</a>
18 21
         </div>
@@ -26,11 +29,10 @@
26 29
     </div>
27 30
     <p></p>
28 31
 
29
-    % if user.profile.id>=3 or h.user_role(user, workspace)>=4:
32
+    % if user.profile.id>=3 or h.user_role(user, workspace)>=4 and file.is_editable:
30 33
         ## if the user can see the toolbar, it means he is the workspace manager.
31 34
         ## So now, we need to know if he alsa has right to delete workspaces
32 35
         <div class="btn-group btn-group-vertical">
33
-            ## SHOW_ARCHIVE_BUTTON__BUG_#81
34 36
             <a title="${_('Archive file')}" class="btn btn-default ${delete_or_archive_disabled}" href="${tg.url('/workspaces/{}/folders/{}/files/{}/put_archive'.format(file.workspace.id, file.parent.id, file.id))}">
35 37
                 ${ICON.FA_FW('fa fa-archive t-less-visible')} ${_('Archive')}
36 38
             </a>
@@ -39,5 +41,28 @@
39 41
             </a>
40 42
         </div>
41 43
     % endif
44
+
45
+    % if file.is_deleted or file.is_archived:
46
+        <div class="btn-group btn-group-vertical">
47
+            % if file.is_archived:
48
+                <a title="${_('Restore')}"
49
+                   class="btn btn-default"
50
+                   href="${tg.url('/workspaces/{}/folders/{}/files/{}/put_archive_undo'.format(file.workspace.id, file.parent.id, file.id))}">
51
+                    <i class="fa fa-archive fa-fw tracim-less-visible"></i>
52
+                    ${_('Restore')}
53
+                </a>
54
+            % endif
55
+            % if file.is_deleted:
56
+                <a title="${_('Restore')}"
57
+                   class="btn btn-default"
58
+                   href="${tg.url('/workspaces/{}/folders/{}/files/{}/put_delete_undo'.format(file.workspace.id, file.parent.id, file.id))}">
59
+                    <i class="fa fa-archive fa-fw tracim-less-visible"></i>
60
+                    ${_('Restore')}
61
+                </a>
62
+            % endif
63
+        </div>
64
+        <p></p>
65
+    % endif
66
+
42 67
 </%def>
43 68
 

+ 8 - 0
tracim/tracim/templates/folder/getone.mak View File

@@ -9,6 +9,7 @@
9 9
 <%namespace name="TABLE_ROW" file="tracim.templates.widgets.table_row"/>
10 10
 <%namespace name="ICON" file="tracim.templates.widgets.icon"/>
11 11
 <%namespace name="P" file="tracim.templates.widgets.paragraph"/>
12
+<%namespace name="UI" file="tracim.templates.widgets.ui"/>
12 13
 
13 14
 <%def name="title()">${result.folder.label}</%def>
14 15
 
@@ -38,6 +39,9 @@
38 39
 ##
39 40
 ############################################################################
40 41
 
42
+<div class="folder-container ${'not-editable' if not result.folder.is_editable else ''}">
43
+<!--# TODO BS 20161213: Indent content-->
44
+
41 45
 <div class="row t-page-header-row">
42 46
     <div class="col-sm-7 col-sm-offset-3 main">
43 47
         <h1 class="page-header t-folder-color-border">
@@ -115,6 +119,8 @@
115 119
                 % endif
116 120
             % endif
117 121
 
122
+            ${UI.GENERIC_DISPLAY_VIEW_BUTTONS_CONTAINER(tg.url('/workspaces/{}/folders/{}'.format(result.folder.workspace.id, result.folder.id)))}
123
+
118 124
             % if len(fake_api.sub_items) <= 0:
119 125
                 ${P.EMPTY_CONTENT(_('This folder has not yet content.'))}
120 126
             % else:
@@ -176,3 +182,5 @@
176 182
         });
177 183
     });
178 184
 </script>
185
+
186
+</div>

+ 26 - 5
tracim/tracim/templates/folder/toolbar.mak View File

@@ -28,7 +28,7 @@
28 28
     %>
29 29
     
30 30
     <% delete_or_archive_disabled = ('', 'disabled')[folder.selected_revision!='latest'] %> 
31
-    % if h.user_role(user, workspace)>2:
31
+    % if h.user_role(user, workspace)>2 and folder.is_editable:
32 32
         <div class="btn-group btn-group-vertical">
33 33
             ## This action is allowed for content managers only
34 34
             <a title="${_('Edit current folder')}" class="btn btn-default ${edit_disabled}" data-toggle="modal" data-target="#folder-edit-modal-dialog" data-remote="${tg.url('/workspaces/{}/folders/{}/edit'.format(folder.workspace.id, folder.id))}" ><i class="fa fa-edit fa-fw tracim-less-visible"></i> ${_('Edit')}</a>
@@ -36,7 +36,7 @@
36 36
         <p></p>
37 37
     % endif
38 38
     
39
-    % if user.profile.id>=3 or h.user_role(user, workspace)>=4:
39
+    % if user.profile.id>=3 or h.user_role(user, workspace)>=4 and folder.is_editable:
40 40
         <div class="btn-group btn-group-vertical">
41 41
             ## This action is allowed for content managers only
42 42
             <a title="${_('Move current folder')}" class="btn btn-default ${move_disabled}" data-toggle="modal" data-target="#folder-move-modal-dialog" data-remote="${tg.url('/workspaces/{}/folders/{}/location/{}/edit'.format(folder.workspace.id, folder.id, folder.id))}" ><i class="fa fa-arrows fa-fw tracim-less-visible"></i> ${_('Move')}</a>
@@ -44,15 +44,36 @@
44 44
         <p></p>
45 45
     % endif
46 46
 
47
-    % if user.profile.id>=3 or h.user_role(user, workspace)>=4:
47
+    % if user.profile.id>=3 or h.user_role(user, workspace)>=4 and folder.is_editable:
48 48
         ## if the user can see the toolbar, it means he is the workspace manager.
49 49
         ## So now, we need to know if he alsa has right to delete workspaces
50 50
         <div class="btn-group btn-group-vertical">
51
-## SHOW_ARCHIVE_BUTTON__BUG_#81            <a title="${_('Archive thread')}" class="btn btn-default ${delete_or_archive_disabled}" href="${tg.url('/workspaces/{}/folders/{}/put_archive'.format(folder.workspace.id, folder.id))}"><i class="fa fa-archive fa-fw tracim-less-visible"></i> ${_('Archive')}</a>
51
+            <a title="${_('Archive thread')}" class="btn btn-default ${delete_or_archive_disabled}" href="${tg.url('/workspaces/{}/folders/{}/put_archive'.format(folder.workspace.id, folder.id))}"><i class="fa fa-archive fa-fw tracim-less-visible"></i> ${_('Archive')}</a>
52 52
             <a title="${_('Delete thread')}" class="btn btn-default ${delete_or_archive_disabled}" href="${tg.url('/workspaces/{}/folders/{}/put_delete'.format(folder.workspace.id, folder.id))}"><i class="fa fa-trash-o fa-fw tracim-less-visible"></i> ${_('Delete')}</a>
53 53
         </div>
54 54
         <p></p>
55 55
     % endif
56 56
 
57
-</%def>
57
+    % if folder.is_deleted or folder.is_archived:
58
+        <div class="btn-group btn-group-vertical">
59
+            % if folder.is_archived:
60
+                <a title="${_('Restore')}"
61
+                   class="btn btn-default"
62
+                   href="${tg.url('/workspaces/{}/folders/{}/put_archive_undo'.format(folder.workspace.id, folder.id))}">
63
+                    <i class="fa fa-archive fa-fw tracim-less-visible"></i>
64
+                    ${_('Restore')}
65
+                </a>
66
+            % endif
67
+            % if folder.is_deleted:
68
+                <a title="${_('Restore')}"
69
+                   class="btn btn-default"
70
+                   href="${tg.url('/workspaces/{}/folders/{}/put_delete_undo'.format(folder.workspace.id, folder.id))}">
71
+                    <i class="fa fa-archive fa-fw tracim-less-visible"></i>
72
+                    ${_('Restore')}
73
+                </a>
74
+            % endif
75
+        </div>
76
+        <p></p>
77
+    % endif
58 78
 
79
+</%def>

+ 3 - 0
tracim/tracim/templates/page/getone.mak View File

@@ -34,6 +34,8 @@
34 34
 ##
35 35
 ############################################################################
36 36
 
37
+<div class="content-container ${'not-editable' if not result.page.is_editable else ''}">
38
+<!--# TODO BS 20161213: Indent content-->
37 39
 
38 40
 <div class="row t-page-header-row">
39 41
     <div class="col-sm-7 col-sm-offset-3 main">
@@ -111,3 +113,4 @@
111 113
     </div>
112 114
 <div/>
113 115
 
116
+</div>

+ 33 - 8
tracim/tracim/templates/page/toolbar.mak View File

@@ -2,15 +2,18 @@
2 2
 <%namespace name="BUTTON" file="tracim.templates.widgets.button"/>
3 3
 
4 4
 <%def name="SECURED_PAGE(user, workspace, page)">
5
-    <div class="btn-group btn-group-vertical">
6
-        ${BUTTON.MARK_CONTENT_READ_OR_UNREAD(user, workspace, page)}
7
-    </div>
8
-    <hr class="t-toolbar-btn-group-separator"/>
9
-    <p></p>
5
+
6
+    % if page.is_editable:
7
+        <div class="btn-group btn-group-vertical">
8
+            ${BUTTON.MARK_CONTENT_READ_OR_UNREAD(user, workspace, page)}
9
+        </div>
10
+        <hr class="t-toolbar-btn-group-separator"/>
11
+        <p></p>
12
+    % endif
10 13
 
11 14
     <% edit_disabled = ('', 'disabled')[page.selected_revision!='latest' or page.status.id[:6]=='closed'] %>
12 15
     <% delete_or_archive_disabled = ('', 'disabled')[page.selected_revision!='latest'] %> 
13
-    % if h.user_role(user, workspace)>1:
16
+    % if h.user_role(user, workspace)>1 and page.is_editable:
14 17
         <div class="btn-group btn-group-vertical">
15 18
             <a title="${_('Edit')}" class="btn btn-default ${edit_disabled}" data-toggle="modal" data-target="#page-edit-modal-dialog" data-remote="${tg.url('/workspaces/{}/folders/{}/pages/{}/edit'.format(page.workspace.id, page.parent.id, page.id))}" >
16 19
                 <i class="fa fa-edit fa-fw t-less-visible"></i> ${_('Edit')}
@@ -29,11 +32,10 @@
29 32
 
30 33
 ## TODO - D.A - 2014-09-16
31 34
 ## Hide the delete button if the user is not a TIM Manager
32
-    % if user.profile.id>=2 or h.user_role(user, workspace)>2:
35
+    % if (user.profile.id>=2 or h.user_role(user, workspace)>2) and page.is_editable:
33 36
         ## if the user can see the toolbar, it means he is the workspace manager.
34 37
         ## So now, we need to know if he alsa has right to delete workspaces
35 38
         <div class="btn-group btn-group-vertical">
36
-            ## SHOW_ARCHIVE_BUTTON__BUG_#81
37 39
             <a title="${_('Archive page')}" class="btn btn-default ${delete_or_archive_disabled}" href="${tg.url('/workspaces/{}/folders/{}/pages/{}/put_archive'.format(page.workspace.id, page.parent.id, page.id))}">
38 40
                 <i class="fa fa-archive fa-fw t-less-visible"></i>
39 41
                 ${_('Archive')}
@@ -45,5 +47,28 @@
45 47
 
46 48
         </div>
47 49
     % endif
50
+
51
+    % if page.is_deleted or page.is_archived:
52
+        <div class="btn-group btn-group-vertical">
53
+            % if page.is_archived:
54
+                <a title="${_('Restore')}"
55
+                   class="btn btn-default"
56
+                   href="${tg.url('/workspaces/{}/folders/{}/pages/{}/put_archive_undo'.format(page.workspace.id, page.parent.id, page.id))}">
57
+                    <i class="fa fa-archive fa-fw tracim-less-visible"></i>
58
+                    ${_('Restore')}
59
+                </a>
60
+            % endif
61
+            % if page.is_deleted:
62
+                <a title="${_('Restore')}"
63
+                   class="btn btn-default"
64
+                   href="${tg.url('/workspaces/{}/folders/{}/pages/{}/put_delete_undo'.format(page.workspace.id, page.parent.id, page.id))}">
65
+                    <i class="fa fa-archive fa-fw tracim-less-visible"></i>
66
+                    ${_('Restore')}
67
+                </a>
68
+            % endif
69
+        </div>
70
+        <p></p>
71
+    % endif
72
+
48 73
 </%def>
49 74
 

+ 5 - 0
tracim/tracim/templates/thread/getone.mak View File

@@ -36,6 +36,9 @@
36 36
 ##
37 37
 ############################################################################
38 38
 
39
+<div class="content-container ${'not-editable' if not result.thread.is_editable else ''}">
40
+<!--# TODO BS 20161213: Indent content-->
41
+
39 42
 <div class="row t-page-header-row">
40 43
     <div class="col-sm-7 col-sm-offset-3 main">
41 44
         <h1 class="page-header t-thread-color-border">
@@ -125,3 +128,5 @@
125 128
 ##     ${WIDGETS.SECURED_TIMELINE_ITEM(fake_api.current_user, comment)}
126 129
 ## % endfor
127 130
 ##
131
+
132
+</div>

+ 37 - 12
tracim/tracim/templates/thread/toolbar.mak View File

@@ -3,15 +3,18 @@
3 3
 <%namespace name="BUTTON" file="tracim.templates.widgets.button"/>
4 4
 
5 5
 <%def name="SECURED_THREAD(user, workspace, thread)">
6
-    <div class="btn-group btn-group-vertical">
7
-        ${BUTTON.MARK_CONTENT_READ_OR_UNREAD(user, workspace, thread)}
8
-    </div>
9
-    <hr class="t-toolbar-btn-group-separator"/>
10
-    <p></p>
6
+
7
+    % if thread.is_editable:
8
+        <div class="btn-group btn-group-vertical">
9
+            ${BUTTON.MARK_CONTENT_READ_OR_UNREAD(user, workspace, thread)}
10
+        </div>
11
+        <hr class="t-toolbar-btn-group-separator"/>
12
+        <p></p>
13
+    % endif
11 14
 
12 15
     <% edit_disabled = ('', 'disabled')[thread.selected_revision!='latest' or thread.status.id[:6]=='closed'] %>
13 16
     <% delete_or_archive_disabled = ('', 'disabled')[thread.selected_revision!='latest'] %> 
14
-    % if h.user_role(user, workspace)>1:
17
+    % if h.user_role(user, workspace)>1 and thread.is_editable:
15 18
         <div class="btn-group btn-group-vertical">
16 19
             <a title="${_('Edit current thread')}" class="btn btn-default ${edit_disabled}" data-toggle="modal" data-target="#thread-edit-modal-dialog" data-remote="${tg.url('/workspaces/{}/folders/{}/threads/{}/edit'.format(thread.workspace.id, thread.parent.id, thread.id))}" >
17 20
                 ${ICON.FA_FW('t-less-visible fa fa-edit')}
@@ -21,20 +24,42 @@
21 24
         <p></p>
22 25
     % endif
23 26
     
24
-    % if user.profile.id>=3 or h.user_role(user, workspace)>=4:
27
+    % if (user.profile.id>=3 or h.user_role(user, workspace)>=4) and thread.is_editable:
25 28
         ## if the user can see the toolbar, it means he is the workspace manager.
26 29
         ## So now, we need to know if he alsa has right to delete workspaces
27 30
         <div class="btn-group btn-group-vertical">
28
-## SHOW_ARCHIVE_BUTTON__BUG_#81
29
-##             <a title="${_('Archive thread')}" class="btn btn-default ${delete_or_archive_disabled}" href="${tg.url('/workspaces/{}/folders/{}/threads/{}/put_archive'.format(thread.workspace.id, thread.parent.id, thread.id))}">
30
-##                 ${ICON.FA_FW('t-less-visible fa fa-archive')}
31
-##                 ${_('Archive')}
32
-##             </a>
31
+            <a title="${_('Archive thread')}" class="btn btn-default ${delete_or_archive_disabled}" href="${tg.url('/workspaces/{}/folders/{}/threads/{}/put_archive'.format(thread.workspace.id, thread.parent.id, thread.id))}">
32
+                ${ICON.FA_FW('t-less-visible fa fa-archive')}
33
+                ${_('Archive')}
34
+            </a>
33 35
             <a title="${_('Delete thread')}" class="btn btn-default ${delete_or_archive_disabled}" href="${tg.url('/workspaces/{}/folders/{}/threads/{}/put_delete'.format(thread.workspace.id, thread.parent.id, thread.id))}">
34 36
                 ${ICON.FA_FW('t-less-visible fa fa-trash')}
35 37
                 ${_('Delete')}
36 38
             </a>
37 39
         </div>
38 40
     % endif
41
+
42
+    % if thread.is_deleted or thread.is_archived:
43
+        <div class="btn-group btn-group-vertical">
44
+            % if thread.is_archived:
45
+                <a title="${_('Restore')}"
46
+                   class="btn btn-default"
47
+                   href="${tg.url('/workspaces/{}/folders/{}/threads/{}/put_archive_undo'.format(thread.workspace.id, thread.parent.id, thread.id))}">
48
+                    <i class="fa fa-archive fa-fw tracim-less-visible"></i>
49
+                    ${_('Restore')}
50
+                </a>
51
+            % endif
52
+            % if thread.is_deleted:
53
+                <a title="${_('Restore')}"
54
+                   class="btn btn-default"
55
+                   href="${tg.url('/workspaces/{}/folders/{}/threads/{}/put_delete_undo'.format(thread.workspace.id, thread.parent.id, thread.id))}">
56
+                    <i class="fa fa-archive fa-fw tracim-less-visible"></i>
57
+                    ${_('Restore')}
58
+                </a>
59
+            % endif
60
+        </div>
61
+        <p></p>
62
+    % endif
63
+
39 64
 </%def>
40 65
 

+ 1 - 1
tracim/tracim/templates/widgets/table_row.mak View File

@@ -55,7 +55,7 @@
55 55
 </%def>
56 56
 
57 57
 <%def name="CONTENT(content)">
58
-    <tr class="t-table-row-${content.type.type}">
58
+    <tr class="t-table-row-${content.type.type} ${'archived' if content.is_archived else ''} ${'deleted' if content.is_deleted else ''}">
59 59
         <td>
60 60
             <span class="${content.type.color}"><i class="fa-fw ${content.type.icon}"></i> ${content.type.label}</span>
61 61
 

+ 32 - 0
tracim/tracim/templates/widgets/ui.mak View File

@@ -0,0 +1,32 @@
1
+<%namespace name="BUTTON" file="tracim.templates.widgets.button"/>
2
+
3
+<%def name="GENERIC_DISPLAY_VIEW_BUTTONS_CONTAINER(base_url)">
4
+    <div class="btn-group" role="group" aria-label="...">
5
+        ${BUTTON.TEXT('', 'btn btn-default disabled', _('display...'))}
6
+        <a href="${base_url}"
7
+           class="btn btn-default disabled-has-priority ${'t-inactive-color' if show_deleted or show_archived else ''}"
8
+        >
9
+            ${_('normal view')}
10
+        </a>
11
+
12
+        % if show_deleted:
13
+        <a href="${base_url}"
14
+           % else:
15
+        <a href="${base_url}?show_deleted=1"
16
+           % endif
17
+           class="btn btn-default disabled-has-priority ${'t-inactive-color' if not show_deleted else ''}"
18
+        >
19
+            ${_('deleted')}
20
+        </a>
21
+
22
+        % if show_archived:
23
+        <a href="${base_url}"
24
+           % else:
25
+        <a href="${base_url}?show_archived=1"
26
+           % endif
27
+           class="btn btn-default disabled-has-priority ${'t-inactive-color' if not show_archived else ''}"
28
+        >
29
+            ${_('archived')}
30
+        </a>
31
+    </div>
32
+</%def>

+ 3 - 0
tracim/tracim/templates/workspace/getone.mak View File

@@ -10,6 +10,7 @@
10 10
 <%namespace name="P" file="tracim.templates.widgets.paragraph"/>
11 11
 <%namespace name="TITLE" file="tracim.templates.widgets.title"/>
12 12
 <%namespace name="TABLE_ROW" file="tracim.templates.widgets.table_row"/>
13
+<%namespace name="UI" file="tracim.templates.widgets.ui"/>
13 14
 
14 15
 <%def name="title()">${result.workspace.label}</%def>
15 16
 
@@ -192,6 +193,8 @@
192 193
                 </div>
193 194
             % endif
194 195
 
196
+            ${UI.GENERIC_DISPLAY_VIEW_BUTTONS_CONTAINER(tg.url('/workspaces/{}'.format(result.workspace.id)))}
197
+
195 198
         </div>
196 199
         <div class="t-spacer-above">
197 200